From 21b75b4c566ed90d2e422245cf85ab4f8316b89f Mon Sep 17 00:00:00 2001 From: SandroB Date: Tue, 20 Jan 2015 15:47:24 +0100 Subject: [PATCH 1/5] vpn changes --- .project | 16 + AndroidManifest.KITKAT_AND_LOWER.xml | 98 +- AndroidManifest.LOLLIPOP.xml | 96 +- AndroidManifest.xml | 106 +- bcdroid.manifest | 6 +- external/Makefile | 4 +- external/appcompat/.classpath | 4 +- jni/Android.mk | 2 +- res/menu/main.xml | 4 +- res/values/strings.xml | 2 +- src/org/torproject/android/Orbot.java | 1416 +++--- .../android/OrbotDiagnosticsActivity.java | 1 + .../android/service/TorResourceInstaller.java | 482 +- .../android/service/TorService.java | 3961 ++++++++--------- .../android/settings/AppManager.java | 5 +- .../android/settings/SettingsPreferences.java | 2 +- .../android/vpn/OrbotVpnService.java | 369 +- src/org/torproject/android/vpn/Tun2Socks.java | 49 +- .../wizard/ChooseLocaleWizardActivity.java | 164 +- .../android/wizard/ConfigureTransProxy.java | 6 +- .../torproject/android/wizard/LotsaText.java | 2 +- .../android/wizard/Permissions.java | 298 +- .../android/wizard/TipsAndTricks.java | 328 +- 23 files changed, 3716 insertions(+), 3705 deletions(-) diff --git a/.project b/.project index 3c3e6b98..2db7e219 100644 --- a/.project +++ b/.project @@ -5,6 +5,12 @@ + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + org.eclipse.wst.jsdt.core.javascriptValidator @@ -30,10 +36,20 @@ + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + com.android.ide.eclipse.adt.AndroidNature org.eclipse.jdt.core.javanature org.eclipse.wst.jsdt.core.jsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature diff --git a/AndroidManifest.KITKAT_AND_LOWER.xml b/AndroidManifest.KITKAT_AND_LOWER.xml index 2e9aa21e..5ba968dd 100644 --- a/AndroidManifest.KITKAT_AND_LOWER.xml +++ b/AndroidManifest.KITKAT_AND_LOWER.xml @@ -3,17 +3,17 @@ package="org.torproject.android" android:versionName="14.1.4-noPIE" android:versionCode="132" - android:installLocation="auto" + android:installLocation="auto" > - + - + - + - - + + - + - + - - - - - - - - - - + + + + + + + + + + - - - - + + + + - - - - - - - - - - - + + + + + + + + + + + - + diff --git a/AndroidManifest.LOLLIPOP.xml b/AndroidManifest.LOLLIPOP.xml index 302aeeba..70841f40 100644 --- a/AndroidManifest.LOLLIPOP.xml +++ b/AndroidManifest.LOLLIPOP.xml @@ -3,17 +3,17 @@ package="org.torproject.android" android:versionName="14.1.4-PIE" android:versionCode="133" - android:installLocation="auto" + android:installLocation="auto" > - + - + - - + + - + - + - - - - - - - - - - + + + + + + + + + + - - - - + + + + - - - - - - - - - - - + + + + + + + + + + + - + diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2e9aa21e..c8cc1f4e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,21 +1,21 @@ - + - + - + - - + + - - - + - + - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + - - - - - - - - - - - + + + + + + + + + + + - + diff --git a/bcdroid.manifest b/bcdroid.manifest index e3467bb0..b6b37f4e 100644 --- a/bcdroid.manifest +++ b/bcdroid.manifest @@ -1,3 +1,3 @@ -Manifest-Version: 1.0 -Sealed: true - +Manifest-Version: 1.0 +Sealed: true + diff --git a/external/Makefile b/external/Makefile index 999c24d1..df7de72c 100644 --- a/external/Makefile +++ b/external/Makefile @@ -16,14 +16,14 @@ EXTERNAL_ROOT := $(shell pwd) # user building this will have to manually set NDK_PROCESSOR or NDK_TOOLCHAIN. CPU := $(shell uname -m) ifeq ($(CPU),x86_64) - NDK_PROCESSOR=x86_64 + NDK_PROCESSOR=x86 else NDK_PROCESSOR=x86 endif # Android NDK setup NDK_BASE ?= /opt/android-ndk -NDK_PLATFORM_LEVEL ?= 9 +NDK_PLATFORM_LEVEL ?= 19 NDK_ABI ?= arm NDK_TOOLCHAIN_VERSION=4.8 NDK_SYSROOT=$(NDK_BASE)/platforms/android-$(NDK_PLATFORM_LEVEL)/arch-$(NDK_ABI) diff --git a/external/appcompat/.classpath b/external/appcompat/.classpath index 2566cbd4..fb2265b7 100644 --- a/external/appcompat/.classpath +++ b/external/appcompat/.classpath @@ -2,10 +2,10 @@ - - + + diff --git a/jni/Android.mk b/jni/Android.mk index edd247b4..4eb1e785 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -1,3 +1,3 @@ ##include ../OriginalDest/Android.mk -##include ../external/badvpn/Android.mk +include ./external/badvpn/Android.mk ##include ../kalium-jni/jni/Android.mk diff --git a/res/menu/main.xml b/res/menu/main.xml index 3c592b2f..8ac6bdea 100644 --- a/res/menu/main.xml +++ b/res/menu/main.xml @@ -36,7 +36,7 @@ android:id="@+id/menu_verify_list" android:title="@string/menu_verify" android:icon="@drawable/ic_action_browse" - yourapp:showAsAction="always" + yourapp:showAsAction="always" > - - Orbot + Ony Orbot is a free proxy app that empowers other apps to use the internet more securely. Orbot uses Tor to encrypt your Internet traffic and then hides it by bouncing through a series of computers around the world. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis. http://orbot/ http://check.torproject.org diff --git a/src/org/torproject/android/Orbot.java b/src/org/torproject/android/Orbot.java index d57eacf5..9f6eb6d8 100644 --- a/src/org/torproject/android/Orbot.java +++ b/src/org/torproject/android/Orbot.java @@ -8,6 +8,7 @@ import static org.torproject.android.TorConstants.TAG; import java.net.URLDecoder; import java.util.Locale; +import org.sandroproxy.ony.R; import org.torproject.android.service.TorService; import org.torproject.android.service.TorServiceConstants; import org.torproject.android.service.TorServiceUtils; @@ -64,11 +65,11 @@ import android.widget.Toast; public class Orbot extends ActionBarActivity implements TorConstants, OnLongClickListener, OnTouchListener, OnSharedPreferenceChangeListener { - /* Useful UI bits */ - private TextView lblStatus = null; //the main text display widget - private ImageProgressView imgStatus = null; //the main touchable image for activating Orbot + /* Useful UI bits */ + private TextView lblStatus = null; //the main text display widget + private ImageProgressView imgStatus = null; //the main touchable image for activating Orbot - private MenuItem mItemOnOff = null; + private MenuItem mItemOnOff = null; private TextView downloadText = null; private TextView uploadText = null; private TextView mTxtOrbotLog = null; @@ -76,191 +77,191 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic private boolean mDrawerOpen = false; private View mViewMain = null; - /* Some tracking bits */ - private int torStatus = TorServiceConstants.STATUS_OFF; //latest status reported from the tor service - - private SharedPreferences mPrefs = null; + /* Some tracking bits */ + private int torStatus = TorServiceConstants.STATUS_OFF; //latest status reported from the tor service + + private SharedPreferences mPrefs = null; - private boolean autoStartFromIntent = false; - - private final static long INIT_DELAY = 100; + private boolean autoStartFromIntent = false; + + private final static long INIT_DELAY = 100; /** Called when the activity is first created. */ - public void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); mPrefs.registerOnSharedPreferenceChangeListener(this); - + setLocale(); - doLayout(); + doLayout(); - appConflictChecker (); - + appConflictChecker (); + - // Register to receive messages. - // We are registering an observer (mMessageReceiver) to receive Intents - // with actions named "custom-event-name". - LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, - new IntentFilter("status")); - - LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, - new IntentFilter("log")); + // Register to receive messages. + // We are registering an observer (mMessageReceiver) to receive Intents + // with actions named "custom-event-name". + LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, + new IntentFilter("status")); + + LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, + new IntentFilter("log")); - mHandler.postDelayed(new Runnable () - { - - public void run () - { - startService(TorServiceConstants.CMD_INIT); - } - },INIT_DELAY); - - } - - // Our handler for received Intents. This will be called whenever an Intent - // with an action named "custom-event-name" is broadcasted. - private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() { - - - - @Override - public void onReceive(Context context, Intent intent) { - // Get extra data included in the Intent - - if (intent.hasExtra("log")) - { - String log = intent.getStringExtra("log"); - updateStatus(log); - } - else if (intent.hasExtra("up")) - { - long upload = intent.getLongExtra("up",0); - long download = intent.getLongExtra("down",0); - long written = intent.getLongExtra("written",0); - long read = intent.getLongExtra("read",0); - - Message msg = mHandler.obtainMessage(TorServiceConstants.MESSAGE_TRAFFIC_COUNT); - msg.getData().putLong("download", download); - msg.getData().putLong("upload", upload); - msg.getData().putLong("readTotal", read); - msg.getData().putLong("writeTotal", written); - mHandler.sendMessage(msg); - - } - else if (intent.hasExtra("status")) - { - torStatus = intent.getIntExtra("status", TorServiceConstants.STATUS_OFF); - updateStatus(""); - } - - } - }; + mHandler.postDelayed(new Runnable () + { + + public void run () + { + startService(TorServiceConstants.CMD_INIT); + } + },INIT_DELAY); + + } + + // Our handler for received Intents. This will be called whenever an Intent + // with an action named "custom-event-name" is broadcasted. + private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() { + + + + @Override + public void onReceive(Context context, Intent intent) { + // Get extra data included in the Intent + + if (intent.hasExtra("log")) + { + String log = intent.getStringExtra("log"); + updateStatus(log); + } + else if (intent.hasExtra("up")) + { + long upload = intent.getLongExtra("up",0); + long download = intent.getLongExtra("down",0); + long written = intent.getLongExtra("written",0); + long read = intent.getLongExtra("read",0); + + Message msg = mHandler.obtainMessage(TorServiceConstants.MESSAGE_TRAFFIC_COUNT); + msg.getData().putLong("download", download); + msg.getData().putLong("upload", upload); + msg.getData().putLong("readTotal", read); + msg.getData().putLong("writeTotal", written); + mHandler.sendMessage(msg); + + } + else if (intent.hasExtra("status")) + { + torStatus = intent.getIntExtra("status", TorServiceConstants.STATUS_OFF); + updateStatus(""); + } + + } + }; - ProgressDialog mProgressDialog; - - private void startService (String action) - { - - Intent torService = new Intent(this, TorService.class); - torService.setAction(action); - startService(torService); - - } - - private void stopService () - { - - Intent torService = new Intent(this, TorService.class); - stopService(torService); - - } - - private void doLayout () - { - setContentView(R.layout.layout_main); - - mViewMain = findViewById(R.id.viewMain); - lblStatus = (TextView)findViewById(R.id.lblStatus); - lblStatus.setOnLongClickListener(this); - imgStatus = (ImageProgressView)findViewById(R.id.imgStatus); - imgStatus.setOnLongClickListener(this); - imgStatus.setOnTouchListener(this); - - lblStatus.setText("Initializing the application..."); - - downloadText = (TextView)findViewById(R.id.trafficDown); + ProgressDialog mProgressDialog; + + private void startService (String action) + { + + Intent torService = new Intent(this, TorService.class); + torService.setAction(action); + startService(torService); + + } + + private void stopService () + { + + Intent torService = new Intent(this, TorService.class); + stopService(torService); + + } + + private void doLayout () + { + setContentView(R.layout.layout_main); + + mViewMain = findViewById(R.id.viewMain); + lblStatus = (TextView)findViewById(R.id.lblStatus); + lblStatus.setOnLongClickListener(this); + imgStatus = (ImageProgressView)findViewById(R.id.imgStatus); + imgStatus.setOnLongClickListener(this); + imgStatus.setOnTouchListener(this); + + lblStatus.setText("Initializing the application..."); + + downloadText = (TextView)findViewById(R.id.trafficDown); uploadText = (TextView)findViewById(R.id.trafficUp); mTxtOrbotLog = (TextView)findViewById(R.id.orbotLog); mDrawer = ((SlidingDrawer)findViewById(R.id.SlidingDrawer)); - Button slideButton = (Button)findViewById(R.id.slideButton); - if (slideButton != null) - { - slideButton.setOnTouchListener(new OnTouchListener (){ - - @Override - public boolean onTouch(View v, MotionEvent event) { - - if (event.equals(MotionEvent.ACTION_DOWN)) - { - mDrawerOpen = !mDrawerOpen; - mTxtOrbotLog.setEnabled(mDrawerOpen); - } - return false; - } - - }); - } - - ScrollingMovementMethod smm = new ScrollingMovementMethod(); - + Button slideButton = (Button)findViewById(R.id.slideButton); + if (slideButton != null) + { + slideButton.setOnTouchListener(new OnTouchListener (){ + + @Override + public boolean onTouch(View v, MotionEvent event) { + + if (event.equals(MotionEvent.ACTION_DOWN)) + { + mDrawerOpen = !mDrawerOpen; + mTxtOrbotLog.setEnabled(mDrawerOpen); + } + return false; + } + + }); + } + + ScrollingMovementMethod smm = new ScrollingMovementMethod(); + mTxtOrbotLog.setMovementMethod(smm); mTxtOrbotLog.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - ClipboardManager cm = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); - cm.setText(mTxtOrbotLog.getText()); - Toast.makeText(Orbot.this, "LOG COPIED TO CLIPBOARD", Toast.LENGTH_SHORT).show(); - return true; - } + @Override + public boolean onLongClick(View v) { + ClipboardManager cm = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); + cm.setText(mTxtOrbotLog.getText()); + Toast.makeText(Orbot.this, "LOG COPIED TO CLIPBOARD", Toast.LENGTH_SHORT).show(); + return true; + } }); - downloadText.setText(formatCount(0) + " / " + formatTotal(0)); - uploadText.setText(formatCount(0) + " / " + formatTotal(0)); - + downloadText.setText(formatCount(0) + " / " + formatTotal(0)); + uploadText.setText(formatCount(0) + " / " + formatTotal(0)); + // Gesture detection - mGestureDetector = new GestureDetector(this, new MyGestureDetector()); - + mGestureDetector = new GestureDetector(this, new MyGestureDetector()); + } - - GestureDetector mGestureDetector; + + GestureDetector mGestureDetector; - @Override - public boolean onTouch(View v, MotionEvent event) { - return mGestureDetector.onTouchEvent(event); + @Override + public boolean onTouch(View v, MotionEvent event) { + return mGestureDetector.onTouchEvent(event); - } - + } + private void appendLogTextAndScroll(String text) { if(mTxtOrbotLog != null && text != null && text.length() > 0){ - - if (mTxtOrbotLog.getText().length() > MAX_LOG_LENGTH) - mTxtOrbotLog.setText(""); - - mTxtOrbotLog.append(text + "\n"); + + if (mTxtOrbotLog.getText().length() > MAX_LOG_LENGTH) + mTxtOrbotLog.setText(""); + + mTxtOrbotLog.append(text + "\n"); final Layout layout = mTxtOrbotLog.getLayout(); if(layout != null){ int scrollDelta = layout.getLineBottom(mTxtOrbotLog.getLineCount() - 1) - mTxtOrbotLog.getScrollY() - mTxtOrbotLog.getHeight(); if(scrollDelta > 0) - mTxtOrbotLog.scrollBy(0, scrollDelta); + mTxtOrbotLog.scrollBy(0, scrollDelta); } } } @@ -283,29 +284,29 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic private void appConflictChecker () { - SharedPreferences sprefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - - boolean showAppConflict = true;//sprefs.getBoolean("pref_show_conflict",true); - - String[] badApps = {"com.sec.msc.nts.android.proxy:com.sec.msc.nts.android.proxy","com.sec.pcw:Samsung Link"}; - - for (String badApp : badApps) - { - String[] badAppParts = badApp.split(":"); - - if (appInstalledOrNot(badAppParts[0])) - { - String msg = getString(R.string.please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_) + badAppParts[1]; - - if (showAppConflict) - showAlert(getString(R.string.app_conflict),msg,true); - - appendLogTextAndScroll(msg); - } - } - - sprefs.edit().putBoolean("pref_show_conflict", false).commit(); - + SharedPreferences sprefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + + boolean showAppConflict = true;//sprefs.getBoolean("pref_show_conflict",true); + + String[] badApps = {"com.sec.msc.nts.android.proxy:com.sec.msc.nts.android.proxy","com.sec.pcw:Samsung Link"}; + + for (String badApp : badApps) + { + String[] badAppParts = badApp.split(":"); + + if (appInstalledOrNot(badAppParts[0])) + { + String msg = getString(R.string.please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_) + badAppParts[1]; + + if (showAppConflict) + showAlert(getString(R.string.app_conflict),msg,true); + + appendLogTextAndScroll(msg); + } + } + + sprefs.edit().putBoolean("pref_show_conflict", false).commit(); + } @@ -314,27 +315,27 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic private void showAbout () { - LayoutInflater li = LayoutInflater.from(this); - View view = li.inflate(R.layout.layout_about, null); - - String version = ""; - - try { - version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName + " (Tor " + TorServiceConstants.BINARY_TOR_VERSION + ")"; - } catch (NameNotFoundException e) { - version = "Version Not Found"; - } - - TextView versionName = (TextView)view.findViewById(R.id.versionName); - versionName.setText(version); - - new AlertDialog.Builder(this) - .setTitle(getString(R.string.button_about)) - .setView(view) - .show(); + LayoutInflater li = LayoutInflater.from(this); + View view = li.inflate(R.layout.layout_about, null); + + String version = ""; + + try { + version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName + " (Tor " + TorServiceConstants.BINARY_TOR_VERSION + ")"; + } catch (NameNotFoundException e) { + version = "Version Not Found"; + } + + TextView versionName = (TextView)view.findViewById(R.id.versionName); + versionName.setText(version); + + new AlertDialog.Builder(this) + .setTitle(getString(R.string.button_about)) + .setView(view) + .show(); } - @Override + @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); @@ -374,7 +375,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic } else if (item.getItemId() == R.id.menu_wizard) { - startActivity(new Intent(this, ChooseLocaleWizardActivity.class)); + startActivity(new Intent(this, ChooseLocaleWizardActivity.class)); } else if (item.getItemId() == R.id.menu_verify) @@ -394,12 +395,10 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic } - /** - * remove for now... VPN is not ready yet else if (item.getItemId() == R.id.menu_vpn) { - this.startVpnService(); - }*/ + this.startVpnService(); + } return true; } @@ -412,7 +411,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic private void doExit () { try { - + //one of the confusing things about all of this code is the multiple //places where things like "stopTor" are called, both in the Activity and the Service //not something to tackle in your first iteration, but i thin we can talk about fixing @@ -432,307 +431,307 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic } /* (non-Javadoc) - * @see android.app.Activity#onPause() - */ - protected void onPause() { - try - { - super.onPause(); - - if (aDialog != null) - aDialog.dismiss(); - } - catch (IllegalStateException ise) - { - //can happen on exit/shutdown - } - } - - private void doTorCheck () - { - - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - switch (which){ - case DialogInterface.BUTTON_POSITIVE: - - openBrowser(URL_TOR_CHECK); + * @see android.app.Activity#onPause() + */ + protected void onPause() { + try + { + super.onPause(); + + if (aDialog != null) + aDialog.dismiss(); + } + catch (IllegalStateException ise) + { + //can happen on exit/shutdown + } + } + + private void doTorCheck () + { + + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + switch (which){ + case DialogInterface.BUTTON_POSITIVE: + + openBrowser(URL_TOR_CHECK); - - - break; + + + break; - case DialogInterface.BUTTON_NEGATIVE: - - //do nothing - break; - } - } - }; + case DialogInterface.BUTTON_NEGATIVE: + + //do nothing + break; + } + } + }; - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(R.string.tor_check).setPositiveButton(R.string.btn_okay, dialogClickListener) - .setNegativeButton(R.string.btn_cancel, dialogClickListener).show(); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(R.string.tor_check).setPositiveButton(R.string.btn_okay, dialogClickListener) + .setNegativeButton(R.string.btn_cancel, dialogClickListener).show(); - } - - private void enableHiddenServicePort (int hsPort) - { - - Editor pEdit = mPrefs.edit(); - - String hsPortString = mPrefs.getString("pref_hs_ports", ""); - - if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1) - hsPortString += ',' + hsPort; - else - hsPortString = hsPort + ""; - - pEdit.putString("pref_hs_ports", hsPortString); - pEdit.putBoolean("pref_hs_enable", true); - - pEdit.commit(); - - String onionHostname = mPrefs.getString("pref_hs_hostname",""); + } + + private void enableHiddenServicePort (int hsPort) + { + + Editor pEdit = mPrefs.edit(); + + String hsPortString = mPrefs.getString("pref_hs_ports", ""); + + if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1) + hsPortString += ',' + hsPort; + else + hsPortString = hsPort + ""; + + pEdit.putString("pref_hs_ports", hsPortString); + pEdit.putBoolean("pref_hs_enable", true); + + pEdit.commit(); + + String onionHostname = mPrefs.getString("pref_hs_hostname",""); - while (onionHostname.length() == 0) - { - //we need to stop and start Tor - try { - stopTor(); - - Thread.sleep(3000); //wait three seconds - - startTor(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - onionHostname = mPrefs.getString("pref_hs_hostname",""); - } - - Intent nResult = new Intent(); - nResult.putExtra("hs_host", onionHostname); - setResult(RESULT_OK, nResult); - - } + while (onionHostname.length() == 0) + { + //we need to stop and start Tor + try { + stopTor(); + + Thread.sleep(3000); //wait three seconds + + startTor(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + onionHostname = mPrefs.getString("pref_hs_hostname",""); + } + + Intent nResult = new Intent(); + nResult.putExtra("hs_host", onionHostname); + setResult(RESULT_OK, nResult); + + } - private synchronized void handleIntents () - { - if (getIntent() == null) - return; - - // Get intent, action and MIME type - Intent intent = getIntent(); - String action = intent.getAction(); - String type = intent.getType(); - - if (action == null) - return; - - if (action.equals("org.torproject.android.REQUEST_HS_PORT")) - { - - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - switch (which){ - case DialogInterface.BUTTON_POSITIVE: - - int hsPort = getIntent().getIntExtra("hs_port", -1); - - enableHiddenServicePort (hsPort); - - finish(); - - - break; + private synchronized void handleIntents () + { + if (getIntent() == null) + return; + + // Get intent, action and MIME type + Intent intent = getIntent(); + String action = intent.getAction(); + String type = intent.getType(); + + if (action == null) + return; + + if (action.equals("org.torproject.android.REQUEST_HS_PORT")) + { + + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + switch (which){ + case DialogInterface.BUTTON_POSITIVE: + + int hsPort = getIntent().getIntExtra("hs_port", -1); + + enableHiddenServicePort (hsPort); + + finish(); + + + break; - case DialogInterface.BUTTON_NEGATIVE: - //No button clicked - finish(); - break; - } - } - }; + case DialogInterface.BUTTON_NEGATIVE: + //No button clicked + finish(); + break; + } + } + }; - int hsPort = getIntent().getIntExtra("hs_port", -1); + int hsPort = getIntent().getIntExtra("hs_port", -1); - String requestMsg = getString(R.string.hidden_service_request, hsPort); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener) - .setNegativeButton("Deny", dialogClickListener).show(); - - - } - else if (action.equals("org.torproject.android.START_TOR")) - { - autoStartFromIntent = true; - - try { - startTor(); + String requestMsg = getString(R.string.hidden_service_request, hsPort); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener) + .setNegativeButton("Deny", dialogClickListener).show(); + + + } + else if (action.equals("org.torproject.android.START_TOR")) + { + autoStartFromIntent = true; + + try { + startTor(); - Intent nResult = new Intent(); - - //nResult.putExtra("socks", ); //TODO respond with socks, transport, dns, etc - - setResult(RESULT_OK,nResult); - - } catch (RemoteException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - else if (action.equals(Intent.ACTION_VIEW)) - { - String urlString = intent.getDataString(); - - if (urlString != null) - { - - if (urlString.toLowerCase().startsWith("bridge://")) + Intent nResult = new Intent(); + + //nResult.putExtra("socks", ); //TODO respond with socks, transport, dns, etc + + setResult(RESULT_OK,nResult); + + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + else if (action.equals(Intent.ACTION_VIEW)) + { + String urlString = intent.getDataString(); + + if (urlString != null) + { + + if (urlString.toLowerCase().startsWith("bridge://")) - { - String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece - newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here - - showAlert("Bridges Updated","Restart Orbot to use this bridge: " + newBridgeValue,false); - - String bridges = mPrefs.getString(TorConstants.PREF_BRIDGES_LIST, null); - - Editor pEdit = mPrefs.edit(); - - if (bridges != null && bridges.trim().length() > 0) - { - if (bridges.indexOf('\n')!=-1) - bridges += '\n' + newBridgeValue; - else - bridges += ',' + newBridgeValue; - } - else - bridges = newBridgeValue; - - pEdit.putString(TorConstants.PREF_BRIDGES_LIST,bridges); //set the string to a preference - pEdit.putBoolean(TorConstants.PREF_BRIDGES_ENABLED,true); - - pEdit.commit(); - - setResult(RESULT_OK); - } - } - } - else - { - - showWizard = mPrefs.getBoolean("show_wizard",showWizard); - - if (showWizard) - { - Editor pEdit = mPrefs.edit(); - pEdit.putBoolean("show_wizard",false); - pEdit.commit(); - showWizard = false; + { + String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece + newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here + + showAlert("Bridges Updated","Restart Orbot to use this bridge: " + newBridgeValue,false); + + String bridges = mPrefs.getString(TorConstants.PREF_BRIDGES_LIST, null); + + Editor pEdit = mPrefs.edit(); + + if (bridges != null && bridges.trim().length() > 0) + { + if (bridges.indexOf('\n')!=-1) + bridges += '\n' + newBridgeValue; + else + bridges += ',' + newBridgeValue; + } + else + bridges = newBridgeValue; + + pEdit.putString(TorConstants.PREF_BRIDGES_LIST,bridges); //set the string to a preference + pEdit.putBoolean(TorConstants.PREF_BRIDGES_ENABLED,true); + + pEdit.commit(); + + setResult(RESULT_OK); + } + } + } + else + { + + showWizard = mPrefs.getBoolean("show_wizard",showWizard); + + if (showWizard) + { + Editor pEdit = mPrefs.edit(); + pEdit.putBoolean("show_wizard",false); + pEdit.commit(); + showWizard = false; - startActivity(new Intent(this, ChooseLocaleWizardActivity.class)); + startActivity(new Intent(this, ChooseLocaleWizardActivity.class)); - } - - } - - setIntent(null); - - updateStatus (""); - - } + } + + } + + setIntent(null); + + updateStatus (""); + + } - private boolean showWizard = true; - - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - doLayout(); - updateStatus(""); - } + private boolean showWizard = true; + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + doLayout(); + updateStatus(""); + } - /* - * Launch the system activity for Uri viewing with the provided url - */ - private void openBrowser(final String browserLaunchUrl) - { - boolean isOrwebInstalled = appInstalledOrNot("info.guardianproject.browser"); - boolean isTransProxy = mPrefs.getBoolean("pref_transparent", false); - - if (isOrwebInstalled) - { - startIntent("info.guardianproject.browser",Intent.ACTION_VIEW,Uri.parse(browserLaunchUrl)); - } - else if (isTransProxy) - { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl)); - intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - else - { - AlertDialog aDialog = new AlertDialog.Builder(Orbot.this) + /* + * Launch the system activity for Uri viewing with the provided url + */ + private void openBrowser(final String browserLaunchUrl) + { + boolean isOrwebInstalled = appInstalledOrNot("info.guardianproject.browser"); + boolean isTransProxy = mPrefs.getBoolean("pref_transparent", false); + + if (isOrwebInstalled) + { + startIntent("info.guardianproject.browser",Intent.ACTION_VIEW,Uri.parse(browserLaunchUrl)); + } + else if (isTransProxy) + { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl)); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + else + { + AlertDialog aDialog = new AlertDialog.Builder(Orbot.this) .setIcon(R.drawable.onion32) - .setTitle(R.string.install_apps_) - .setMessage(R.string.it_doesn_t_seem_like_you_have_orweb_installed_want_help_with_that_or_should_we_just_open_the_browser_) - .setPositiveButton(android.R.string.ok, new OnClickListener () - { + .setTitle(R.string.install_apps_) + .setMessage(R.string.it_doesn_t_seem_like_you_have_orweb_installed_want_help_with_that_or_should_we_just_open_the_browser_) + .setPositiveButton(android.R.string.ok, new OnClickListener () + { - @Override - public void onClick(DialogInterface dialog, int which) { + @Override + public void onClick(DialogInterface dialog, int which) { - //prompt to install Orweb - Intent intent = new Intent(Orbot.this,TipsAndTricks.class); - startActivity(intent); - - } - - }) - .setNegativeButton(android.R.string.no, new OnClickListener () - { + //prompt to install Orweb + Intent intent = new Intent(Orbot.this,TipsAndTricks.class); + startActivity(intent); + + } + + }) + .setNegativeButton(android.R.string.no, new OnClickListener () + { - @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl)); - intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - - } - - }) - .show(); - - } - - } - - private void startIntent (String pkg, String action, Uri data) - { - Intent i; - PackageManager manager = getPackageManager(); - try { - i = manager.getLaunchIntentForPackage(pkg); - if (i == null) - throw new PackageManager.NameNotFoundException(); - i.setAction(action); - i.setData(data); - startActivity(i); - } catch (PackageManager.NameNotFoundException e) { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl)); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + + } + + }) + .show(); + + } + + } + + private void startIntent (String pkg, String action, Uri data) + { + Intent i; + PackageManager manager = getPackageManager(); + try { + i = manager.getLaunchIntentForPackage(pkg); + if (i == null) + throw new PackageManager.NameNotFoundException(); + i.setAction(action); + i.setData(data); + startActivity(i); + } catch (PackageManager.NameNotFoundException e) { - } - } - - private boolean appInstalledOrNot(String uri) + } + } + + private boolean appInstalledOrNot(String uri) { PackageManager pm = getPackageManager(); try @@ -745,7 +744,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic return false; } } - + /* * Load the basic settings application to display torrc */ @@ -757,95 +756,95 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic @Override - protected void onActivityResult(int request, int response, Intent data) { - super.onActivityResult(request, response, data); - - - if (request == 1 && response == RESULT_OK) - { - if (data != null && data.getBooleanExtra("transproxywipe", false)) - { - - boolean result = flushTransProxy(); - - if (result) - { + protected void onActivityResult(int request, int response, Intent data) { + super.onActivityResult(request, response, data); + + + if (request == 1 && response == RESULT_OK) + { + if (data != null && data.getBooleanExtra("transproxywipe", false)) + { + + boolean result = flushTransProxy(); + + if (result) + { - Toast.makeText(this, R.string.transparent_proxy_rules_flushed_, Toast.LENGTH_SHORT).show(); - - } - else - { + Toast.makeText(this, R.string.transparent_proxy_rules_flushed_, Toast.LENGTH_SHORT).show(); + + } + else + { - Toast.makeText(this, R.string.you_do_not_have_root_access_enabled, Toast.LENGTH_SHORT).show(); - - } - - } - else if (torStatus == TorServiceConstants.STATUS_ON) - { - updateSettings(); - Toast.makeText(this, R.string.you_may_need_to_stop_and_start_orbot_for_settings_change_to_be_enabled_, Toast.LENGTH_SHORT).show(); + Toast.makeText(this, R.string.you_do_not_have_root_access_enabled, Toast.LENGTH_SHORT).show(); + + } + + } + else if (torStatus == TorServiceConstants.STATUS_ON) + { + updateSettings(); + Toast.makeText(this, R.string.you_may_need_to_stop_and_start_orbot_for_settings_change_to_be_enabled_, Toast.LENGTH_SHORT).show(); - } - } - else if (request == REQUEST_VPN && response == RESULT_OK) - { - startService(TorServiceConstants.CMD_VPN); - } - - } + } + } + else if (request == REQUEST_VPN && response == RESULT_OK) + { + startService(TorServiceConstants.CMD_VPN); + } + + } private final static int REQUEST_VPN = 8888; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - public void startVpnService () + public void startVpnService () { - Intent intent = VpnService.prepare(this); - if (intent != null) { - startActivityForResult(intent,REQUEST_VPN); - } - else - { - startService(TorServiceConstants.CMD_VPN); + Intent intent = VpnService.prepare(this); + if (intent != null) { + startActivityForResult(intent,REQUEST_VPN); + } + else + { + startService(TorServiceConstants.CMD_VPN); - } + } } private boolean flushTransProxy () { - startService(TorServiceConstants.CMD_FLUSH); - return true; + startService(TorServiceConstants.CMD_FLUSH); + return true; } private boolean updateSettings () { - //todo send service command - startService(TorServiceConstants.CMD_UPDATE); - return true; + //todo send service command + startService(TorServiceConstants.CMD_UPDATE); + return true; } - @Override - protected void onResume() { - super.onResume(); + @Override + protected void onResume() { + super.onResume(); - mHandler.postDelayed(new Runnable () - { - public void run () - { + mHandler.postDelayed(new Runnable () + { + public void run () + { - setLocale(); - - handleIntents(); + setLocale(); + + handleIntents(); - } - } - , 500); - - - } + } + } + , 500); + + + } - AlertDialog aDialog = null; + AlertDialog aDialog = null; //general alert dialog for mostly Tor warning messages //sometimes this can go haywire or crazy with too many error @@ -881,7 +880,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic aDialog.setCanceledOnTouchOutside(true); } - private void updateStatus (String torServiceMsg) + private void updateStatus (String torServiceMsg) { //now update the layout_main UI based on the status @@ -890,9 +889,9 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic if (torStatus == TorServiceConstants.STATUS_ON) { - + imgStatus.setImageResource(R.drawable.toron); - + String lblMsg = getString(R.string.status_activated); lblStatus.setText(lblMsg); @@ -902,7 +901,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic if (torServiceMsg != null && torServiceMsg.length() > 0) { - appendLogTextAndScroll(torServiceMsg); + appendLogTextAndScroll(torServiceMsg); } boolean showFirstTime = mPrefs.getBoolean("connect_first_time",true); @@ -923,23 +922,23 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic if (autoStartFromIntent) { - setResult(RESULT_OK); - finish(); + setResult(RESULT_OK); + finish(); } } else if (torStatus == TorServiceConstants.STATUS_CONNECTING) { - + imgStatus.setImageResource(R.drawable.torstarting); if (mItemOnOff != null) mItemOnOff.setTitle(R.string.menu_stop); - - + + if (lblStatus != null && torServiceMsg != null) - if (torServiceMsg.indexOf('%')!=-1) - lblStatus.setText(torServiceMsg); + if (torServiceMsg.indexOf('%')!=-1) + lblStatus.setText(torServiceMsg); appendLogTextAndScroll(torServiceMsg); @@ -958,7 +957,7 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic } - + // guess what? this start's Tor! actually no it just requests via the local ITorService to the remote TorService instance @@ -967,10 +966,10 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic { - startService (TorServiceConstants.CMD_START); - torStatus = TorServiceConstants.STATUS_CONNECTING; - - mTxtOrbotLog.setText(""); + startService (TorServiceConstants.CMD_START); + torStatus = TorServiceConstants.STATUS_CONNECTING; + + mTxtOrbotLog.setText(""); //here we update the UI which is a bit sloppy and mixed up code wise //might be best to just call updateStatus() instead of directly manipulating UI in this method - yep makes sense @@ -983,21 +982,21 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic mHandler.sendMessage(msg); - + } //now we stop Tor! amazing! private void stopTor () throws RemoteException { - - startService (TorServiceConstants.CMD_STOP); - torStatus = TorServiceConstants.STATUS_OFF; + + startService (TorServiceConstants.CMD_STOP); + torStatus = TorServiceConstants.STATUS_OFF; - Message msg = mHandler.obtainMessage(TorServiceConstants.DISABLE_TOR_MSG); - mHandler.sendMessage(msg); - + Message msg = mHandler.obtainMessage(TorServiceConstants.DISABLE_TOR_MSG); + mHandler.sendMessage(msg); + - + } /* @@ -1006,48 +1005,48 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic */ public boolean onLongClick(View view) { - if (!mDrawerOpen) - { - try - { - - if (torStatus == TorServiceConstants.STATUS_OFF) - { - - startTor(); - } - else - { - - stopTor(); - stopService (); - - } - - return true; - - } - catch (Exception e) - { - Log.d(TAG,"error onclick",e); - } + if (!mDrawerOpen) + { + try + { + + if (torStatus == TorServiceConstants.STATUS_OFF) + { + + startTor(); + } + else + { + + stopTor(); + stopService (); + + } + + return true; + + } + catch (Exception e) + { + Log.d(TAG,"error onclick",e); + } - } - + } + return false; } - + // this is what takes messages or values from the callback threads or other non-mainUI threads //and passes them back into the main UI thread for display to the user private Handler mHandler = new Handler() { - - private String lastServiceMsg = null; - + + private String lastServiceMsg = null; + public void handleMessage(Message msg) { switch (msg.what) { case TorServiceConstants.STATUS_MSG: @@ -1057,9 +1056,9 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic if (lastServiceMsg == null || !lastServiceMsg.equals(torServiceMsg)) { - updateStatus(torServiceMsg); + updateStatus(torServiceMsg); - lastServiceMsg = torServiceMsg; + lastServiceMsg = torServiceMsg; } break; @@ -1070,28 +1069,28 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic break; case TorServiceConstants.DISABLE_TOR_MSG: - - updateStatus((String)msg.getData().getString(HANDLER_TOR_MSG)); - - break; - - - case TorServiceConstants.MESSAGE_TRAFFIC_COUNT : - Bundle data = msg.getData(); - DataCount datacount = new DataCount(data.getLong("upload"),data.getLong("download")); - - long totalRead = data.getLong("readTotal"); - long totalWrite = data.getLong("writeTotal"); - - downloadText.setText(formatCount(datacount.Download) + " / " + formatTotal(totalRead)); - uploadText.setText(formatCount(datacount.Upload) + " / " + formatTotal(totalWrite)); + updateStatus((String)msg.getData().getString(HANDLER_TOR_MSG)); + + break; + + + case TorServiceConstants.MESSAGE_TRAFFIC_COUNT : + + Bundle data = msg.getData(); + DataCount datacount = new DataCount(data.getLong("upload"),data.getLong("download")); + + long totalRead = data.getLong("readTotal"); + long totalWrite = data.getLong("writeTotal"); + + downloadText.setText(formatCount(datacount.Download) + " / " + formatTotal(totalRead)); + uploadText.setText(formatCount(datacount.Upload) + " / " + formatTotal(totalWrite)); - if (torStatus != TorServiceConstants.STATUS_ON) - { - updateStatus(""); - } - + if (torStatus != TorServiceConstants.STATUS_ON) + { + updateStatus(""); + } + default: super.handleMessage(msg); } @@ -1111,107 +1110,104 @@ public class Orbot extends ActionBarActivity implements TorConstants, OnLongClic private void setLocale () { - + Configuration config = getResources().getConfiguration(); String lang = mPrefs.getString(PREF_DEFAULT_LOCALE, ""); if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang)) { - Locale locale = new Locale(lang); + Locale locale = new Locale(lang); Locale.setDefault(locale); config.locale = locale; getResources().updateConfiguration(config, getResources().getDisplayMetrics()); } } - @Override - protected void onDestroy() { - super.onDestroy(); - LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver); + @Override + protected void onDestroy() { + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver); - } + } - public class DataCount { - // data uploaded - public long Upload; - // data downloaded - public long Download; - - DataCount(long Upload, long Download){ - this.Upload = Upload; - this.Download = Download; - } - } - - private String formatCount(long count) { - // Converts the supplied argument into a string. - // Under 2Mb, returns "xxx.xKb" - // Over 2Mb, returns "xxx.xxMb" - if (count < 1e6) - return ((float)((int)(count*10/1024))/10 + "kbps"); - return ((float)((int)(count*100/1024/1024))/100 + "mbps"); - - //return count+" kB"; - } - - private String formatTotal(long count) { - // Converts the supplied argument into a string. - // Under 2Mb, returns "xxx.xKb" - // Over 2Mb, returns "xxx.xxMb" - if (count < 1e6) - return ((float)((int)(count*10/1024))/10 + "KB"); - return ((float)((int)(count*100/1024/1024))/100 + "MB"); - - //return count+" kB"; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - - - } - - private static final float ROTATE_FROM = 0.0f; - private static final float ROTATE_TO = 360.0f*4f;// 3.141592654f * 32.0f; - - public void spinOrbot (float direction) - { - startService (TorServiceConstants.CMD_NEWNYM); - - - Toast.makeText(this, R.string.newnym, Toast.LENGTH_SHORT).show(); - - // Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); - Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, imgStatus.getWidth()/2f,imgStatus.getWidth()/2f,20f,false); - rotation.setFillAfter(true); - rotation.setInterpolator(new AccelerateInterpolator()); - rotation.setDuration((long) 2*1000); - rotation.setRepeatCount(0); - imgStatus.startAnimation(rotation); - - - } - - class MyGestureDetector extends SimpleOnGestureListener { - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - try { - if (torStatus == TorServiceConstants.STATUS_ON) - { - float direction = 1f; - if (velocityX < 0) - direction = -1f; - spinOrbot (direction); - } - } catch (Exception e) { - // nothing - } - return false; - } - - } + public class DataCount { + // data uploaded + public long Upload; + // data downloaded + public long Download; + + DataCount(long Upload, long Download){ + this.Upload = Upload; + this.Download = Download; + } + } + + private String formatCount(long count) { + // Converts the supplied argument into a string. + // Under 2Mb, returns "xxx.xKb" + // Over 2Mb, returns "xxx.xxMb" + if (count < 1e6) + return ((float)((int)(count*10/1024))/10 + "kbps"); + return ((float)((int)(count*100/1024/1024))/100 + "mbps"); + + //return count+" kB"; + } + + private String formatTotal(long count) { + // Converts the supplied argument into a string. + // Under 2Mb, returns "xxx.xKb" + // Over 2Mb, returns "xxx.xxMb" + if (count < 1e6) + return ((float)((int)(count*10/1024))/10 + "KB"); + return ((float)((int)(count*100/1024/1024))/100 + "MB"); + + //return count+" kB"; + } + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + + + } + + private static final float ROTATE_FROM = 0.0f; + private static final float ROTATE_TO = 360.0f*4f;// 3.141592654f * 32.0f; + public void spinOrbot (float direction) + { + startService (TorServiceConstants.CMD_NEWNYM); + + + Toast.makeText(this, R.string.newnym, Toast.LENGTH_SHORT).show(); + + // Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, imgStatus.getWidth()/2f,imgStatus.getWidth()/2f,20f,false); + rotation.setFillAfter(true); + rotation.setInterpolator(new AccelerateInterpolator()); + rotation.setDuration((long) 2*1000); + rotation.setRepeatCount(0); + imgStatus.startAnimation(rotation); + + + } + + class MyGestureDetector extends SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + try { + if (torStatus == TorServiceConstants.STATUS_ON) + { + float direction = 1f; + if (velocityX < 0) + direction = -1f; + spinOrbot (direction); + } + } catch (Exception e) { + // nothing + } + return false; + } + } } diff --git a/src/org/torproject/android/OrbotDiagnosticsActivity.java b/src/org/torproject/android/OrbotDiagnosticsActivity.java index b639d828..995e99a7 100644 --- a/src/org/torproject/android/OrbotDiagnosticsActivity.java +++ b/src/org/torproject/android/OrbotDiagnosticsActivity.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import org.sandroproxy.ony.R; import org.sufficientlysecure.rootcommands.Shell; import org.sufficientlysecure.rootcommands.command.SimpleCommand; import org.torproject.android.service.TorResourceInstaller; diff --git a/src/org/torproject/android/service/TorResourceInstaller.java b/src/org/torproject/android/service/TorResourceInstaller.java index e4b0ba47..d10ccdb4 100644 --- a/src/org/torproject/android/service/TorResourceInstaller.java +++ b/src/org/torproject/android/service/TorResourceInstaller.java @@ -17,9 +17,9 @@ import java.util.concurrent.TimeoutException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import org.sandroproxy.ony.R; import org.sufficientlysecure.rootcommands.Shell; import org.sufficientlysecure.rootcommands.command.SimpleCommand; -import org.torproject.android.R; import org.torproject.android.TorConstants; import android.content.Context; @@ -28,44 +28,44 @@ import android.util.Log; public class TorResourceInstaller implements TorServiceConstants { - - File installFolder; - Context context; - - public TorResourceInstaller (Context context, File installFolder) - { - this.installFolder = installFolder; - - this.context = context; - } - - public void deleteDirectory(File file) { - if( file.exists() ) { - if (file.isDirectory()) { - File[] files = file.listFiles(); - for(int i=0; i(),installFolder.getAbsolutePath()); - is = context.getResources().openRawResource(R.raw.torrc); - outFile = new File(installFolder, TORRC_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, false); - - is = context.getResources().openRawResource(R.raw.torpolipo); - outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, false); - - - is = context.getResources().openRawResource(R.raw.tor); - outFile = new File(installFolder, TOR_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, true); - - is = context.getResources().openRawResource(R.raw.polipo); - outFile = new File(installFolder, POLIPO_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, true); - - is = context.getResources().openRawResource(R.raw.obfsclient); - outFile = new File(installFolder, OBFSCLIENT_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, true); - - is = context.getResources().openRawResource(R.raw.xtables); - outFile = new File(installFolder, IPTABLES_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, true); + is = context.getResources().openRawResource(R.raw.torrc); + outFile = new File(installFolder, TORRC_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, false); + + is = context.getResources().openRawResource(R.raw.torpolipo); + outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, false); - - return true; - } - - public boolean updateTorConfigCustom (File fileTorRcCustom, String extraLines) throws IOException, FileNotFoundException, TimeoutException - { - + + is = context.getResources().openRawResource(R.raw.tor); + outFile = new File(installFolder, TOR_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, true); + + is = context.getResources().openRawResource(R.raw.polipo); + outFile = new File(installFolder, POLIPO_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, true); + + is = context.getResources().openRawResource(R.raw.obfsclient); + outFile = new File(installFolder, OBFSCLIENT_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, true); + + is = context.getResources().openRawResource(R.raw.xtables); + outFile = new File(installFolder, IPTABLES_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, true); + + + return true; + } + + public boolean updateTorConfigCustom (File fileTorRcCustom, String extraLines) throws IOException, FileNotFoundException, TimeoutException + { + - StringBufferInputStream sbis = new StringBufferInputStream(extraLines + '\n'); - streamToFile(sbis,fileTorRcCustom,false,false); - - return true; - } - - public boolean updatePolipoConfig (File filePolipo, String extraLines) throws IOException, FileNotFoundException, TimeoutException - { - - InputStream is; + StringBufferInputStream sbis = new StringBufferInputStream(extraLines + '\n'); + streamToFile(sbis,fileTorRcCustom,false,false); + + return true; + } + + public boolean updatePolipoConfig (File filePolipo, String extraLines) throws IOException, FileNotFoundException, TimeoutException + { + + InputStream is; Shell shell = Shell.startShell(new ArrayList(),installFolder.getAbsolutePath()); - is = context.getResources().openRawResource(R.raw.torpolipo); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + filePolipo.getAbsolutePath())).waitForFinish(); - streamToFile(is,filePolipo, false, false); + is = context.getResources().openRawResource(R.raw.torpolipo); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + filePolipo.getAbsolutePath())).waitForFinish(); + streamToFile(is,filePolipo, false, false); - if (extraLines != null && extraLines.length() > 0) - { - StringBufferInputStream sbis = new StringBufferInputStream('\n' + extraLines + '\n'); - streamToFile(sbis,filePolipo,true,false); - } - - shell.close(); - - return true; - } - - public boolean installPolipoConf () throws IOException, FileNotFoundException, TimeoutException - { - - InputStream is; + if (extraLines != null && extraLines.length() > 0) + { + StringBufferInputStream sbis = new StringBufferInputStream('\n' + extraLines + '\n'); + streamToFile(sbis,filePolipo,true,false); + } + + shell.close(); + + return true; + } + + public boolean installPolipoConf () throws IOException, FileNotFoundException, TimeoutException + { + + InputStream is; File outFile; Shell shell = Shell.startShell(new ArrayList(),installFolder.getAbsolutePath()); is = context.getResources().openRawResource(R.raw.torpolipo); - outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY); - shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); - streamToFile(is,outFile, false, false); - - return true; - } - - /* - * Extract the Tor binary from the APK file using ZIP - */ - - public boolean installGeoIP () throws IOException, FileNotFoundException - { - - InputStream is; + outFile = new File(installFolder, POLIPOCONFIG_ASSET_KEY); + shell.add(new SimpleCommand(COMMAND_RM_FORCE + outFile.getAbsolutePath())).waitForFinish(); + streamToFile(is,outFile, false, false); + + return true; + } + + /* + * Extract the Tor binary from the APK file using ZIP + */ + + public boolean installGeoIP () throws IOException, FileNotFoundException + { + + InputStream is; File outFile; - is = context.getResources().openRawResource(R.raw.geoip); - outFile = new File(installFolder, GEOIP_ASSET_KEY); - streamToFile(is, outFile, false, true); - - is = context.getResources().openRawResource(R.raw.geoip6); - outFile = new File(installFolder, GEOIP6_ASSET_KEY); - streamToFile(is, outFile, false, true); - - return true; - } - - /* - private static void copyAssetFile(Context ctx, String asset, File file) throws IOException, InterruptedException - { - - DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); - InputStream is = new GZIPInputStream(ctx.getAssets().open(asset)); - - byte buf[] = new byte[8172]; - int len; - while ((len = is.read(buf)) > 0) { - out.write(buf, 0, len); - } - out.close(); - is.close(); - }*/ - - /* - * Write the inputstream contents to the file - */ + is = context.getResources().openRawResource(R.raw.geoip); + outFile = new File(installFolder, GEOIP_ASSET_KEY); + streamToFile(is, outFile, false, true); + + is = context.getResources().openRawResource(R.raw.geoip6); + outFile = new File(installFolder, GEOIP6_ASSET_KEY); + streamToFile(is, outFile, false, true); + + return true; + } + + /* + private static void copyAssetFile(Context ctx, String asset, File file) throws IOException, InterruptedException + { + + DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); + InputStream is = new GZIPInputStream(ctx.getAssets().open(asset)); + + byte buf[] = new byte[8172]; + int len; + while ((len = is.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + is.close(); + }*/ + + /* + * Write the inputstream contents to the file + */ public static boolean streamToFile(InputStream stm, File outFile, boolean append, boolean zip) throws IOException { @@ -204,17 +204,17 @@ public class TorResourceInstaller implements TorServiceConstants { int bytecount; - OutputStream stmOut = new FileOutputStream(outFile.getAbsolutePath(), append); - ZipInputStream zis = null; - - if (zip) - { - zis = new ZipInputStream(stm); - ZipEntry ze = zis.getNextEntry(); - stm = zis; - - } - + OutputStream stmOut = new FileOutputStream(outFile.getAbsolutePath(), append); + ZipInputStream zis = null; + + if (zip) + { + zis = new ZipInputStream(stm); + ZipEntry ze = zis.getNextEntry(); + stm = zis; + + } + while ((bytecount = stm.read(buffer)) > 0) { @@ -226,97 +226,97 @@ public class TorResourceInstaller implements TorServiceConstants { stm.close(); if (zis != null) - zis.close(); + zis.close(); return true; } - + //copy the file from inputstream to File output - alternative impl - public static void copyFile (InputStream is, File outputFile) - { - - try { - outputFile.createNewFile(); - DataOutputStream out = new DataOutputStream(new FileOutputStream(outputFile)); - DataInputStream in = new DataInputStream(is); - - int b = -1; - byte[] data = new byte[1024]; - - while ((b = in.read(data)) != -1) { - out.write(data); - } - - if (b == -1); //rejoice - - // - out.flush(); - out.close(); - in.close(); - // chmod? - - - - } catch (IOException ex) { - Log.e(TorConstants.TAG, "error copying binary", ex); - } + public static void copyFile (InputStream is, File outputFile) + { + + try { + outputFile.createNewFile(); + DataOutputStream out = new DataOutputStream(new FileOutputStream(outputFile)); + DataInputStream in = new DataInputStream(is); + + int b = -1; + byte[] data = new byte[1024]; + + while ((b = in.read(data)) != -1) { + out.write(data); + } + + if (b == -1); //rejoice + + // + out.flush(); + out.close(); + in.close(); + // chmod? + + + + } catch (IOException ex) { + Log.e(TorConstants.TAG, "error copying binary", ex); + } - } - - + } + + - /** - * Copies a raw resource file, given its ID to the given location - * @param ctx context - * @param resid resource id - * @param file destination file - * @param mode file permissions (E.g.: "755") - * @throws IOException on error - * @throws InterruptedException when interrupted - */ - public static void copyRawFile(Context ctx, int resid, File file, String mode, boolean isZipd) throws IOException, InterruptedException - { - final String abspath = file.getAbsolutePath(); - // Write the iptables binary - final FileOutputStream out = new FileOutputStream(file); - InputStream is = ctx.getResources().openRawResource(resid); - - if (isZipd) - { - ZipInputStream zis = new ZipInputStream(is); - ZipEntry ze = zis.getNextEntry(); - is = zis; - } - - byte buf[] = new byte[1024]; - int len; - while ((len = is.read(buf)) > 0) { - out.write(buf, 0, len); - } - out.close(); - is.close(); - // Change the permissions - Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor(); - } /** - * Asserts that the binary files are installed in the bin directory. - * @param ctx context + * Copies a raw resource file, given its ID to the given location + * @param ctx context + * @param resid resource id + * @param file destination file + * @param mode file permissions (E.g.: "755") + * @throws IOException on error + * @throws InterruptedException when interrupted + */ + public static void copyRawFile(Context ctx, int resid, File file, String mode, boolean isZipd) throws IOException, InterruptedException + { + final String abspath = file.getAbsolutePath(); + // Write the iptables binary + final FileOutputStream out = new FileOutputStream(file); + InputStream is = ctx.getResources().openRawResource(resid); + + if (isZipd) + { + ZipInputStream zis = new ZipInputStream(is); + ZipEntry ze = zis.getNextEntry(); + is = zis; + } + + byte buf[] = new byte[1024]; + int len; + while ((len = is.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + is.close(); + // Change the permissions + Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor(); + } + /** + * Asserts that the binary files are installed in the bin directory. + * @param ctx context * @param showErrors indicates if errors should be alerted - * @return false if the binary files could not be installed - */ - /* - public static boolean assertIpTablesBinaries(Context ctx, boolean showErrors) throws Exception { - boolean changed = false; - - // Check iptables_g1 - File file = new File(ctx.getDir("bin",0), "iptables"); - copyRawFile(ctx, R.raw.iptables, file, CHMOD_EXEC, false); - - return true; - }*/ - + * @return false if the binary files could not be installed + */ + /* + public static boolean assertIpTablesBinaries(Context ctx, boolean showErrors) throws Exception { + boolean changed = false; + + // Check iptables_g1 + File file = new File(ctx.getDir("bin",0), "iptables"); + copyRawFile(ctx, R.raw.iptables, file, CHMOD_EXEC, false); + + return true; + }*/ + } diff --git a/src/org/torproject/android/service/TorService.java b/src/org/torproject/android/service/TorService.java index 63bf0d5e..251137f0 100644 --- a/src/org/torproject/android/service/TorService.java +++ b/src/org/torproject/android/service/TorService.java @@ -45,10 +45,10 @@ import net.freehaven.tor.control.TorControlConnection; import org.json.JSONArray; import org.json.JSONObject; +import org.sandroproxy.ony.R; import org.sufficientlysecure.rootcommands.Shell; import org.sufficientlysecure.rootcommands.command.SimpleCommand; import org.torproject.android.Orbot; -import org.torproject.android.R; import org.torproject.android.TorConstants; import org.torproject.android.Utils; import org.torproject.android.settings.AppManager; @@ -82,30 +82,30 @@ import android.widget.RemoteViews; public class TorService extends Service implements TorServiceConstants, TorConstants, EventHandler { - - public static boolean ENABLE_DEBUG_LOG = false; - - private int mCurrentStatus = STATUS_OFF; - - private final static int CONTROL_SOCKET_TIMEOUT = 0; - - private TorControlConnection conn = null; - private Socket torConnSocket = null; - private int mLastProcessId = -1; - + + public static boolean ENABLE_DEBUG_LOG = false; + + private int mCurrentStatus = STATUS_OFF; + + private final static int CONTROL_SOCKET_TIMEOUT = 0; + + private TorControlConnection conn = null; + private Socket torConnSocket = null; + private int mLastProcessId = -1; + - private int mPortHTTP = 8118; - private int mPortSOCKS = 9050; - - - private static final int NOTIFY_ID = 1; - private static final int TRANSPROXY_NOTIFY_ID = 2; - private static final int ERROR_NOTIFY_ID = 3; - private static final int HS_NOTIFY_ID = 4; - - private boolean prefPersistNotifications = true; - - private static final int MAX_START_TRIES = 3; + private int mPortHTTP = 8118; + private int mPortSOCKS = 9050; + + + private static final int NOTIFY_ID = 1; + private static final int TRANSPROXY_NOTIFY_ID = 2; + private static final int ERROR_NOTIFY_ID = 3; + private static final int HS_NOTIFY_ID = 4; + + private boolean prefPersistNotifications = true; + + private static final int MAX_START_TRIES = 3; private ArrayList configBuffer = null; private ArrayList resetBuffer = null; @@ -124,18 +124,18 @@ public class TorService extends Service implements TorServiceConstants, TorConst private TorTransProxy mTransProxy; - private long mTotalTrafficWritten = 0; - private long mTotalTrafficRead = 0; - private boolean mConnectivity = true; + private long mTotalTrafficWritten = 0; + private long mTotalTrafficRead = 0; + private boolean mConnectivity = true; - private long lastRead = -1; - private long lastWritten = -1; - - private NotificationManager mNotificationManager = null; - private Builder mNotifyBuilder; - private Notification mNotification; - private boolean mShowExpandedNotifications = false; - private boolean mNotificationShowing = false; + private long lastRead = -1; + private long lastWritten = -1; + + private NotificationManager mNotificationManager = null; + private Builder mNotifyBuilder; + private Notification mNotification; + private boolean mShowExpandedNotifications = false; + private boolean mNotificationShowing = false; private boolean mHasRoot = false; private boolean mEnableTransparentProxy = false; @@ -147,592 +147,561 @@ public class TorService extends Service implements TorServiceConstants, TorConst public void debug(String msg) { - if (ENABLE_DEBUG_LOG) - { - Log.d(TAG,msg); - sendCallbackLogMessage(msg); + if (ENABLE_DEBUG_LOG) + { + Log.d(TAG,msg); + sendCallbackLogMessage(msg); - } + } } public void logException(String msg, Exception e) { - if (ENABLE_DEBUG_LOG) - { - Log.e(TAG,msg,e); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - e.printStackTrace(new PrintStream(baos)); - - sendCallbackLogMessage(msg + '\n'+ new String(baos.toByteArray())); - - } - else - sendCallbackLogMessage(msg); - + if (ENABLE_DEBUG_LOG) + { + Log.e(TAG,msg,e); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(baos)); + + sendCallbackLogMessage(msg + '\n'+ new String(baos.toByteArray())); + + } + else + sendCallbackLogMessage(msg); + } private boolean findExistingProc () { - if (fileTor != null) - { - try - { - - mLastProcessId = initControlConnection(3,true); - - if (mLastProcessId != -1 && conn != null) - { - sendCallbackLogMessage (getString(R.string.found_existing_tor_process)); - - mCurrentStatus = STATUS_ON; - sendCallbackStatus(mCurrentStatus); - - return true; - } - - - return false; - } - catch (Exception e) - { - //Log.e(TAG,"error finding proc",e); - return false; - } - } - else - return false; + if (fileTor != null) + { + try + { + + mLastProcessId = initControlConnection(3,true); + + if (mLastProcessId != -1 && conn != null) + { + sendCallbackLogMessage (getString(R.string.found_existing_tor_process)); + + mCurrentStatus = STATUS_ON; + sendCallbackStatus(mCurrentStatus); + + return true; + } + + + return false; + } + catch (Exception e) + { + //Log.e(TAG,"error finding proc",e); + return false; + } + } + else + return false; } /* (non-Javadoc) - * @see android.app.Service#onLowMemory() - */ + * @see android.app.Service#onLowMemory() + */ @Override - public void onLowMemory() { - super.onLowMemory(); - - logNotice( "Low Memory Warning!"); - - } - - - public int getTorStatus () - { - - return mCurrentStatus; - + public void onLowMemory() { + super.onLowMemory(); + + logNotice( "Low Memory Warning!"); + } - - private void clearNotifications () - { - if (mNotificationManager != null) - mNotificationManager.cancelAll(); - - hmBuiltNodes.clear(); - mNotificationShowing = false; - - } - - @SuppressLint("NewApi") - private void showToolbarNotification (String notifyMsg, int notifyType, int icon) - { - - //Reusable code. - Intent intent = new Intent(TorService.this, Orbot.class); - PendingIntent pendIntent = PendingIntent.getActivity(TorService.this, 0, intent, 0); + + public int getTorStatus () + { + + return mCurrentStatus; + + } + + private void clearNotifications () + { + if (mNotificationManager != null) + mNotificationManager.cancelAll(); + + + hmBuiltNodes.clear(); + mNotificationShowing = false; + + } + + @SuppressLint("NewApi") + private void showToolbarNotification (String notifyMsg, int notifyType, int icon) + { + + //Reusable code. + Intent intent = new Intent(TorService.this, Orbot.class); + PendingIntent pendIntent = PendingIntent.getActivity(TorService.this, 0, intent, 0); - if (mNotifyBuilder == null) - { - - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - if (mNotifyBuilder == null) - { - mNotifyBuilder = new NotificationCompat.Builder(this) - .setContentTitle(getString(R.string.app_name)) - .setSmallIcon(R.drawable.ic_stat_tor); + if (mNotifyBuilder == null) + { + + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + if (mNotifyBuilder == null) + { + mNotifyBuilder = new NotificationCompat.Builder(this) + .setContentTitle(getString(R.string.app_name)) + .setSmallIcon(R.drawable.ic_stat_tor); - mNotifyBuilder.setContentIntent(pendIntent); - } - - } + mNotifyBuilder.setContentIntent(pendIntent); + } + + } - mNotifyBuilder.setContentText(notifyMsg); - mNotifyBuilder.setSmallIcon(icon); - - if (notifyType != NOTIFY_ID) - { - mNotifyBuilder.setTicker(notifyMsg); - // mNotifyBuilder.setLights(Color.GREEN, 1000, 1000); - } - else - { - mNotifyBuilder.setTicker(null); - } - - mNotifyBuilder.setOngoing(prefPersistNotifications); - - mNotification = mNotifyBuilder.build(); - - if (Build.VERSION.SDK_INT >= 16 && mShowExpandedNotifications) { - - - // Create remote view that needs to be set as bigContentView for the notification. - RemoteViews expandedView = new RemoteViews(this.getPackageName(), - R.layout.layout_notification_expanded); - - StringBuffer sbInfo = new StringBuffer(); - - - if (notifyType == NOTIFY_ID) - expandedView.setTextViewText(R.id.text, notifyMsg); - else - { - expandedView.setTextViewText(R.id.info, notifyMsg); - - } + mNotifyBuilder.setContentText(notifyMsg); + mNotifyBuilder.setSmallIcon(icon); + + if (notifyType != NOTIFY_ID) + { + mNotifyBuilder.setTicker(notifyMsg); + // mNotifyBuilder.setLights(Color.GREEN, 1000, 1000); + } + else + { + mNotifyBuilder.setTicker(null); + } + + mNotifyBuilder.setOngoing(prefPersistNotifications); + + mNotification = mNotifyBuilder.build(); + + if (Build.VERSION.SDK_INT >= 16 && mShowExpandedNotifications) { + + + // Create remote view that needs to be set as bigContentView for the notification. + RemoteViews expandedView = new RemoteViews(this.getPackageName(), + R.layout.layout_notification_expanded); + + StringBuffer sbInfo = new StringBuffer(); + + + if (notifyType == NOTIFY_ID) + expandedView.setTextViewText(R.id.text, notifyMsg); + else + { + expandedView.setTextViewText(R.id.info, notifyMsg); + + } - if (hmBuiltNodes.size() > 0) - { - //sbInfo.append(getString(R.string.your_tor_public_ips_) + '\n'); - - Set itBuiltNodes = hmBuiltNodes.keySet(); - for (String key : itBuiltNodes) - { - Node node = hmBuiltNodes.get(key); - - if (node.ipAddress != null) - { - sbInfo.append(node.ipAddress); - - if (node.country != null) - sbInfo.append(' ').append(node.country); - - if (node.organization != null) - sbInfo.append(" (").append(node.organization).append(')'); - - sbInfo.append('\n'); - } - - } - - expandedView.setTextViewText(R.id.text2, sbInfo.toString()); - } - - expandedView.setTextViewText(R.id.title, getString(R.string.app_name)); - - expandedView.setImageViewResource(R.id.icon, icon); - mNotification.bigContentView = expandedView; - } - - if (prefPersistNotifications && (!mNotificationShowing)) - { - startForeground(NOTIFY_ID, mNotification); - logNotice("Set background service to FOREGROUND"); - } - else - { - mNotificationManager.notify(NOTIFY_ID, mNotification); - } - - mNotificationShowing = true; - } + if (hmBuiltNodes.size() > 0) + { + //sbInfo.append(getString(R.string.your_tor_public_ips_) + '\n'); + + Set itBuiltNodes = hmBuiltNodes.keySet(); + for (String key : itBuiltNodes) + { + Node node = hmBuiltNodes.get(key); + + if (node.ipAddress != null) + { + sbInfo.append(node.ipAddress); + + if (node.country != null) + sbInfo.append(' ').append(node.country); + + if (node.organization != null) + sbInfo.append(" (").append(node.organization).append(')'); + + sbInfo.append('\n'); + } + + } + + expandedView.setTextViewText(R.id.text2, sbInfo.toString()); + } + + expandedView.setTextViewText(R.id.title, getString(R.string.app_name)); + + expandedView.setImageViewResource(R.id.icon, icon); + mNotification.bigContentView = expandedView; + } + + if (prefPersistNotifications && (!mNotificationShowing)) + { + startForeground(NOTIFY_ID, mNotification); + logNotice("Set background service to FOREGROUND"); + } + else + { + mNotificationManager.notify(NOTIFY_ID, mNotification); + } + + mNotificationShowing = true; + } - /* (non-Javadoc) - * @see android.app.Service#onStart(android.content.Intent, int) - */ - public int onStartCommand(Intent intent, int flags, int startId) { + /* (non-Javadoc) + * @see android.app.Service#onStart(android.content.Intent, int) + */ + public int onStartCommand(Intent intent, int flags, int startId) { - new Thread (new TorStarter(intent)).start(); - - return START_REDELIVER_INTENT; + new Thread (new TorStarter(intent)).start(); + + return START_REDELIVER_INTENT; - } - - private class TorStarter implements Runnable - { - Intent mIntent; - - public TorStarter (Intent intent) - { - mIntent = intent; - } - - public void run () - { - try - { - - //if this is a start on boot launch turn tor on - if (mIntent != null) - { - String action = mIntent.getAction(); - - if (action!=null) - { - if(action.equals(Intent.ACTION_BOOT_COMPLETED)||action.equals(CMD_START)) - { - setTorProfile(STATUS_ON); - } - else if (action.equals(CMD_STOP)) - { - setTorProfile(STATUS_OFF); - } - else if (action.equals(CMD_INIT)) - { - initialize(); - sendCallbackStatus(mCurrentStatus); - } - else if (action.equals(CMD_NEWNYM)) - { - newIdentity(); - } - else if (action.equals(CMD_FLUSH)) - { - flushTransparentProxyRules(); - } - else if (action.equals(CMD_UPDATE)) - { - processSettings(); - } - else if (action.equals(CMD_VPN)) - { - startVpnService(); - } - } - } - else - { - Log.d(TAG, "Got null onStartCommand() intent"); - } - - } - catch (Exception e) - { - Log.e(TAG,"error onBind",e); - } - } - } - + } + + private class TorStarter implements Runnable + { + Intent mIntent; + + public TorStarter (Intent intent) + { + mIntent = intent; + } + + public void run (){ + try{ + //if this is a start on boot launch turn tor on + if (mIntent != null){ + String action = mIntent.getAction(); + + if (action!=null){ + if(action.equals(Intent.ACTION_BOOT_COMPLETED)||action.equals(CMD_START)){ + setTorProfile(STATUS_ON); + }else if (action.equals(CMD_STOP)){ + setTorProfile(STATUS_OFF); + }else if (action.equals(CMD_INIT)){ + initialize(); + sendCallbackStatus(mCurrentStatus); + }else if (action.equals(CMD_NEWNYM)){ + newIdentity(); + }else if (action.equals(CMD_FLUSH)){ + flushTransparentProxyRules(); + }else if (action.equals(CMD_UPDATE)){ + processSettings(); + }else if (action.equals(CMD_VPN)){ + startVpnService(); + } + } + }else{ + Log.d(TAG, "Got null onStartCommand() intent"); + } + + }catch (Exception e){ + Log.e(TAG,"error onBind",e); + } + } + } + @Override - public void onTaskRemoved(Intent rootIntent) { - Log.d(TAG,"task removed"); - - Intent intent = new Intent( this, DummyActivity.class ); - intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); - startActivity( intent ); - } + public void onTaskRemoved(Intent rootIntent){ + Log.d(TAG,"task removed"); + Intent intent = new Intent( this, DummyActivity.class ); + intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); + startActivity( intent ); + } @Override - public boolean stopService(Intent name) { - - logNotice("TorService is being stopped: " + name); + public boolean stopService(Intent name) { + logNotice("TorService is being stopped: " + name); + return super.stopService(name); + } - return super.stopService(name); - - } - - @Override + @Override public void onDestroy () { - String msg = ("TorService is being DESTROYED... shutting down!"); - - Log.d(TAG, msg); - sendCallbackLogMessage(msg); - - unregisterReceiver(mNetworkStateReceiver); - + String msg = ("TorService is being DESTROYED... shutting down!"); + Log.d(TAG, msg); + sendCallbackLogMessage(msg); + unregisterReceiver(mNetworkStateReceiver); clearNotifications (); - - super.onDestroy(); - + super.onDestroy(); } private void stopTor () { - - try - { - Log.d(TAG,"Tor is stopping NOW"); - - shutdownTorProcess (); - - //stop the foreground priority and make sure to remove the persistant notification - stopForeground(true); - - mCurrentStatus = STATUS_OFF; - sendCallbackStatus(mCurrentStatus); - - if (mHasRoot && mEnableTransparentProxy) - disableTransparentProxy(Shell.startRootShell()); - - clearNotifications(); - - sendCallbackLogMessage(getString(R.string.status_disabled)); + + try + { + Log.d(TAG,"Tor is stopping NOW"); + + shutdownTorProcess (); + + //stop the foreground priority and make sure to remove the persistant notification + stopForeground(true); + + mCurrentStatus = STATUS_OFF; + sendCallbackStatus(mCurrentStatus); + + if (mHasRoot && mEnableTransparentProxy) + disableTransparentProxy(Shell.startRootShell()); + + clearNotifications(); + + sendCallbackLogMessage(getString(R.string.status_disabled)); - } - catch (Exception e) - { - Log.d(TAG, "An error occured stopping Tor",e); - logNotice("An error occured stopping Tor: " + e.getMessage()); - sendCallbackLogMessage(getString(R.string.something_bad_happened)); + } + catch (Exception e) + { + Log.d(TAG, "An error occured stopping Tor",e); + logNotice("An error occured stopping Tor: " + e.getMessage()); + sendCallbackLogMessage(getString(R.string.something_bad_happened)); - } + } } - private String getHiddenServiceHostname () - { + private String getHiddenServiceHostname () + { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false); StringBuffer result = new StringBuffer(); - + if (enableHiddenServices) { - String hsPorts = prefs.getString("pref_hs_ports",""); - - StringTokenizer st = new StringTokenizer (hsPorts,","); - String hsPortConfig = null; - - while (st.hasMoreTokens()) - { - - int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);; - - File fileDir = new File(appCacheHome, "hs" + hsPort); - File file = new File(fileDir, "hostname"); - - - if (file.exists()) - { - try { - String onionHostname = Utils.readString(new FileInputStream(file)).trim(); - - if (result.length() > 0) - result.append(","); - - result.append(onionHostname); - - - } catch (FileNotFoundException e) { - logException("unable to read onion hostname file",e); - showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr); - return null; - } - } - else - { - showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr); - return null; - - } - } + String hsPorts = prefs.getString("pref_hs_ports",""); + + StringTokenizer st = new StringTokenizer (hsPorts,","); + String hsPortConfig = null; + + while (st.hasMoreTokens()) + { + + int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);; + + File fileDir = new File(appCacheHome, "hs" + hsPort); + File file = new File(fileDir, "hostname"); + + + if (file.exists()) + { + try { + String onionHostname = Utils.readString(new FileInputStream(file)).trim(); + + if (result.length() > 0) + result.append(","); + + result.append(onionHostname); + + + } catch (FileNotFoundException e) { + logException("unable to read onion hostname file",e); + showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + return null; + } + } + else + { + showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + return null; + + } + } + + if (result.length() > 0) + { + String onionHostname = result.toString(); + + showToolbarNotification(getString(R.string.hidden_service_on) + ' ' + onionHostname, HS_NOTIFY_ID, R.drawable.ic_stat_tor); + Editor pEdit = prefs.edit(); + pEdit.putString("pref_hs_hostname",onionHostname); + pEdit.commit(); + + return onionHostname; + } - if (result.length() > 0) - { - String onionHostname = result.toString(); - - showToolbarNotification(getString(R.string.hidden_service_on) + ' ' + onionHostname, HS_NOTIFY_ID, R.drawable.ic_stat_tor); - Editor pEdit = prefs.edit(); - pEdit.putString("pref_hs_hostname",onionHostname); - pEdit.commit(); - - return onionHostname; - } - } return null; - } - - + } + + private void shutdownTorProcess () throws Exception { - if (conn != null) - { + if (conn != null) + { - logNotice("Using control port to shutdown Tor"); - - - try { - logNotice("sending HALT signal to Tor process"); - conn.shutdownTor("HALT"); - - } catch (Exception e) { - Log.d(TAG,"error shutting down Tor via connection",e); - } - - conn = null; - } - else - killProcess(fileTor); - - killProcess(filePolipo); - // killProcess(fileObfsclient); - + logNotice("Using control port to shutdown Tor"); + + + try { + logNotice("sending HALT signal to Tor process"); + conn.shutdownTor("HALT"); + + } catch (Exception e) { + Log.d(TAG,"error shutting down Tor via connection",e); + } + + conn = null; + } + else + killProcess(fileTor); + + killProcess(filePolipo); + // killProcess(fileObfsclient); + } private void killProcess (File fileProcBin) throws IOException { - int procId = -1; - Shell shell = Shell.startShell(); - - while ((procId = TorServiceUtils.findProcessId(fileProcBin.getCanonicalPath())) != -1) - { - - logNotice("Found " + fileProcBin.getName() + " PID=" + procId + " - killing now..."); - - SimpleCommand killCommand = new SimpleCommand("toolbox kill " + procId); - shell.add(killCommand); - killCommand = new SimpleCommand("kill " + procId); - shell.add(killCommand); - } - - shell.close(); + int procId = -1; + Shell shell = Shell.startShell(); + + while ((procId = TorServiceUtils.findProcessId(fileProcBin.getCanonicalPath())) != -1) + { + + logNotice("Found " + fileProcBin.getName() + " PID=" + procId + " - killing now..."); + + SimpleCommand killCommand = new SimpleCommand("toolbox kill " + procId); + shell.add(killCommand); + killCommand = new SimpleCommand("kill " + procId); + shell.add(killCommand); + } + + shell.close(); } private void logNotice (String msg) { - if (msg != null && msg.trim().length() > 0) - { - if (ENABLE_DEBUG_LOG) - Log.d(TAG, msg); - - sendCallbackLogMessage(msg); - } + if (msg != null && msg.trim().length() > 0) + { + if (ENABLE_DEBUG_LOG) + Log.d(TAG, msg); + + sendCallbackLogMessage(msg); + } } @Override - public void onCreate() { - super.onCreate(); - initialize(); + public void onCreate() { + super.onCreate(); + initialize(); } private void initialize() { - try - { + try + { - if (mNotificationManager == null) - { - - IntentFilter mNetworkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - registerReceiver(mNetworkStateReceiver , mNetworkStateFilter); - - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - } - - - initBinariesAndDirectories(); - updateSettings(); - - new Thread(new Runnable () - { - public void run () - { - try - { - findExistingProc (); - } - catch (Exception e) - { - Log.e(TAG,"error onBind",e); - logNotice("error finding exiting process: " + e.toString()); - } - - } - }).start(); - - } - catch (Exception e) - { - //what error here - Log.e(TAG, "Error installing Orbot binaries",e); - logNotice("There was an error installing Orbot binaries"); - } - - - } + if (mNotificationManager == null) + { + + IntentFilter mNetworkStateFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(mNetworkStateReceiver , mNetworkStateFilter); + + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + } + + + initBinariesAndDirectories(); + updateSettings(); + + new Thread(new Runnable () + { + public void run () + { + try + { + findExistingProc (); + } + catch (Exception e) + { + Log.e(TAG,"error onBind",e); + logNotice("error finding exiting process: " + e.toString()); + } + + } + }).start(); + + } + catch (Exception e) + { + //what error here + Log.e(TAG, "Error installing Orbot binaries",e); + logNotice("There was an error installing Orbot binaries"); + } + + + } - private void initBinariesAndDirectories () throws Exception + private void initBinariesAndDirectories () throws Exception { - if (appBinHome == null) - appBinHome = getDir(DIRECTORY_TOR_BINARY,Application.MODE_PRIVATE); - - if (appCacheHome == null) - appCacheHome = getDir(DIRECTORY_TOR_DATA,Application.MODE_PRIVATE); - - fileTor= new File(appBinHome, TOR_ASSET_KEY); - - filePolipo = new File(appBinHome, POLIPO_ASSET_KEY); - - fileObfsclient = new File(appBinHome, OBFSCLIENT_ASSET_KEY); - - fileTorRc = new File(appBinHome, TORRC_ASSET_KEY); - - fileXtables = new File(appBinHome, IPTABLES_ASSET_KEY); - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - String version = prefs.getString(PREF_BINARY_TOR_VERSION_INSTALLED,null); + if (appBinHome == null) + appBinHome = getDir(DIRECTORY_TOR_BINARY,Application.MODE_PRIVATE); + + if (appCacheHome == null) + appCacheHome = getDir(DIRECTORY_TOR_DATA,Application.MODE_PRIVATE); + + fileTor= new File(appBinHome, TOR_ASSET_KEY); + + filePolipo = new File(appBinHome, POLIPO_ASSET_KEY); + + fileObfsclient = new File(appBinHome, OBFSCLIENT_ASSET_KEY); + + fileTorRc = new File(appBinHome, TORRC_ASSET_KEY); + + fileXtables = new File(appBinHome, IPTABLES_ASSET_KEY); + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + String version = prefs.getString(PREF_BINARY_TOR_VERSION_INSTALLED,null); - logNotice("checking binary version: " + version); - - TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); - - if (version == null || (!version.equals(BINARY_TOR_VERSION)) || (!fileTor.exists())) - { - logNotice("upgrading binaries to latest version: " + BINARY_TOR_VERSION); - - boolean success = installer.installResources(); - - if (success) - prefs.edit().putString(PREF_BINARY_TOR_VERSION_INSTALLED,BINARY_TOR_VERSION).commit(); - } + logNotice("checking binary version: " + version); + + TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); + + if (version == null || (!version.equals(BINARY_TOR_VERSION)) || (!fileTor.exists())) + { + logNotice("upgrading binaries to latest version: " + BINARY_TOR_VERSION); + + boolean success = installer.installResources(); + + if (success) + prefs.edit().putString(PREF_BINARY_TOR_VERSION_INSTALLED,BINARY_TOR_VERSION).commit(); + } - updateTorConfigFile (); - + updateTorConfigFile (); + } private boolean updateTorConfigFile () throws FileNotFoundException, IOException, TimeoutException { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); - - StringBuffer extraLines = new StringBuffer(); - - String TORRC_CONTROLPORT_FILE_KEY = "ControlPortWriteToFile"; - fileControlPort = new File(appBinHome,"control.txt"); - extraLines.append(TORRC_CONTROLPORT_FILE_KEY).append(' ').append(fileControlPort.getCanonicalPath()).append('\n'); - - if (mTransProxyTethering) - { - extraLines.append("TransListenAddress 0.0.0.0").append('\n'); - extraLines.append("DNSListenAddress 0.0.0.0").append('\n'); - - } - - extraLines.append("RunAsDaemon 1").append('\n'); - extraLines.append("AvoidDiskWrites 1").append('\n'); + TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); - extraLines.append("SOCKSPort ").append("auto").append('\n'); - extraLines.append("SafeSocks 0").append('\n'); - extraLines.append("TestSocks 0").append('\n'); - extraLines.append("WarnUnsafeSocks 1").append('\n'); + StringBuffer extraLines = new StringBuffer(); + + String TORRC_CONTROLPORT_FILE_KEY = "ControlPortWriteToFile"; + fileControlPort = new File(appBinHome,"control.txt"); + extraLines.append(TORRC_CONTROLPORT_FILE_KEY).append(' ').append(fileControlPort.getCanonicalPath()).append('\n'); + + if (mTransProxyTethering) + { + extraLines.append("TransListenAddress 0.0.0.0").append('\n'); + extraLines.append("DNSListenAddress 0.0.0.0").append('\n'); + + } + + extraLines.append("RunAsDaemon 1").append('\n'); + extraLines.append("AvoidDiskWrites 1").append('\n'); + + extraLines.append("SOCKSPort ").append("auto").append('\n'); + extraLines.append("SafeSocks 0").append('\n'); + extraLines.append("TestSocks 0").append('\n'); + extraLines.append("WarnUnsafeSocks 1").append('\n'); - extraLines.append("TransPort ").append("auto").append('\n'); - extraLines.append("DNSPort ").append("auto").append('\n'); + extraLines.append("TransPort ").append("auto").append('\n'); + extraLines.append("DNSPort ").append("auto").append('\n'); extraLines.append("VirtualAddrNetwork 10.192.0.0/10").append('\n'); extraLines.append("AutomapHostsOnResolve 1").append('\n'); @@ -740,134 +709,134 @@ public class TorService extends Service implements TorServiceConstants, TorConst extraLines.append("CircuitStreamTimeout 60").append('\n'); - extraLines.append(prefs.getString("pref_custom_torrc", "")); + extraLines.append(prefs.getString("pref_custom_torrc", "")); - logNotice("updating torrc custom configuration..."); + logNotice("updating torrc custom configuration..."); - debug("torrc.custom=" + extraLines.toString()); - - File fileTorRcCustom = new File(fileTorRc.getAbsolutePath() + ".custom"); - boolean success = installer.updateTorConfigCustom(fileTorRcCustom, extraLines.toString()); - - if (success) - { - logNotice ("success."); - } - - return success; + debug("torrc.custom=" + extraLines.toString()); + + File fileTorRcCustom = new File(fileTorRc.getAbsolutePath() + ".custom"); + boolean success = installer.updateTorConfigCustom(fileTorRcCustom, extraLines.toString()); + + if (success) + { + logNotice ("success."); + } + + return success; } private boolean enableBinExec (File fileBin) throws Exception { - - logNotice(fileBin.getName() + ": PRE: Is binary exec? " + fileBin.canExecute()); + + logNotice(fileBin.getName() + ": PRE: Is binary exec? " + fileBin.canExecute()); - if (!fileBin.canExecute()) - { - logNotice("(re)Setting permission on binary: " + fileBin.getCanonicalPath()); - - Shell shell = Shell.startShell(); - shell.add(new SimpleCommand("chmod " + CHMOD_EXE_VALUE + ' ' + fileBin.getCanonicalPath())).waitForFinish(); - - File fileTest = new File(fileBin.getCanonicalPath()); - logNotice(fileTest.getName() + ": POST: Is binary exec? " + fileTest.canExecute()); - - shell.close(); - } - - return fileBin.canExecute(); + if (!fileBin.canExecute()) + { + logNotice("(re)Setting permission on binary: " + fileBin.getCanonicalPath()); + + Shell shell = Shell.startShell(); + shell.add(new SimpleCommand("chmod " + CHMOD_EXE_VALUE + ' ' + fileBin.getCanonicalPath())).waitForFinish(); + + File fileTest = new File(fileBin.getCanonicalPath()); + logNotice(fileTest.getName() + ": POST: Is binary exec? " + fileTest.canExecute()); + + shell.close(); + } + + return fileBin.canExecute(); } private void updateSettings () throws TimeoutException, IOException { - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - mHasRoot = prefs.getBoolean(PREF_HAS_ROOT,false); - - mEnableTransparentProxy = prefs.getBoolean("pref_transparent", false); - mTransProxyAll = prefs.getBoolean("pref_transparent_all", false); - mTransProxyTethering = prefs.getBoolean("pref_transparent_tethering", false); - mTransProxyNetworkRefresh = prefs.getBoolean("pref_transproxy_refresh", false); - - mShowExpandedNotifications = prefs.getBoolean("pref_expanded_notifications", false); - - ENABLE_DEBUG_LOG = prefs.getBoolean("pref_enable_logging",false); - Log.i(TAG,"debug logging:" + ENABLE_DEBUG_LOG); + mHasRoot = prefs.getBoolean(PREF_HAS_ROOT,false); + + mEnableTransparentProxy = prefs.getBoolean("pref_transparent", false); + mTransProxyAll = prefs.getBoolean("pref_transparent_all", false); + mTransProxyTethering = prefs.getBoolean("pref_transparent_tethering", false); + mTransProxyNetworkRefresh = prefs.getBoolean("pref_transproxy_refresh", false); + + mShowExpandedNotifications = prefs.getBoolean("pref_expanded_notifications", false); + + ENABLE_DEBUG_LOG = prefs.getBoolean("pref_enable_logging",false); + Log.i(TAG,"debug logging:" + ENABLE_DEBUG_LOG); - prefPersistNotifications = prefs.getBoolean(TorConstants.PREF_PERSIST_NOTIFICATIONS, true); - + prefPersistNotifications = prefs.getBoolean(TorConstants.PREF_PERSIST_NOTIFICATIONS, true); + } private void startTor () throws Exception { - - mCurrentStatus = STATUS_CONNECTING; - sendCallbackStatus(mCurrentStatus); - - if (fileTor == null) - initBinariesAndDirectories(); - - enableBinExec(fileTor); - enableBinExec(filePolipo); - enableBinExec(fileObfsclient); - enableBinExec(fileXtables); - - updateSettings (); + + mCurrentStatus = STATUS_CONNECTING; + sendCallbackStatus(mCurrentStatus); + + if (fileTor == null) + initBinariesAndDirectories(); + + enableBinExec(fileTor); + enableBinExec(filePolipo); + enableBinExec(fileObfsclient); + enableBinExec(fileXtables); + + updateSettings (); - logNotice(getString(R.string.status_starting_up)); - sendCallbackLogMessage(getString(R.string.status_starting_up)); - - Shell shellUser = Shell.startShell(); - - boolean success = runTorShellCmd(shellUser); - - if (success) - { - if (mPortHTTP != -1) - runPolipoShellCmd(shellUser); - - if (mHasRoot && mEnableTransparentProxy) - { - Shell shellRoot = Shell.startRootShell(); + logNotice(getString(R.string.status_starting_up)); + sendCallbackLogMessage(getString(R.string.status_starting_up)); + + Shell shellUser = Shell.startShell(); + + boolean success = runTorShellCmd(shellUser); + + if (success) + { + if (mPortHTTP != -1) + runPolipoShellCmd(shellUser); + + if (mHasRoot && mEnableTransparentProxy) + { + Shell shellRoot = Shell.startRootShell(); - disableTransparentProxy(shellRoot); - enableTransparentProxy(shellRoot); - - shellRoot.close(); - } - - getHiddenServiceHostname (); - } - else - { - showToolbarNotification(getString(R.string.unable_to_start_tor), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + disableTransparentProxy(shellRoot); + enableTransparentProxy(shellRoot); + + shellRoot.close(); + } + + getHiddenServiceHostname (); + } + else + { + showToolbarNotification(getString(R.string.unable_to_start_tor), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); - } - - shellUser.close(); + } + + shellUser.close(); } private boolean flushTransparentProxyRules () throws Exception { - if (mHasRoot) - { - if (mTransProxy == null) - { - mTransProxy = new TorTransProxy(this, fileXtables); - - } - - mTransProxy.flushTransproxyRules(this); - - return true; - } - else - { - return false; - } + if (mHasRoot) + { + if (mTransProxy == null) + { + mTransProxy = new TorTransProxy(this, fileXtables); + + } + + mTransProxy.flushTransproxyRules(this); + + return true; + } + else + { + return false; + } } /* @@ -877,68 +846,68 @@ public class TorService extends Service implements TorServiceConstants, TorConst * the idea is that if Tor is off then transproxy is off */ private boolean enableTransparentProxy (Shell shell) throws Exception - { - - if (mTransProxy == null) - { - mTransProxy = new TorTransProxy(this, fileXtables); - - } + { + + if (mTransProxy == null) + { + mTransProxy = new TorTransProxy(this, fileXtables); + + } - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - String transProxy = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); - String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); - - if (transProxy.indexOf(':')!=-1) //we just want the port for this - transProxy = transProxy.split(":")[1]; - - if (dnsPort.indexOf(':')!=-1) //we just want the port for this - dnsPort = dnsPort.split(":")[1]; - - mTransProxy.setTransProxyPort(Integer.parseInt(transProxy)); - mTransProxy.setDNSPort(Integer.parseInt(dnsPort)); - - int code = 0; // Default state is "okay" - - if(mTransProxyAll) - { + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + String transProxy = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); + String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); + + if (transProxy.indexOf(':')!=-1) //we just want the port for this + transProxy = transProxy.split(":")[1]; + + if (dnsPort.indexOf(':')!=-1) //we just want the port for this + dnsPort = dnsPort.split(":")[1]; + + mTransProxy.setTransProxyPort(Integer.parseInt(transProxy)); + mTransProxy.setDNSPort(Integer.parseInt(dnsPort)); + + int code = 0; // Default state is "okay" + + if(mTransProxyAll) + { - code = mTransProxy.setTransparentProxyingAll(this, true, shell); - } - else - { - ArrayList apps = AppManager.getApps(this, TorServiceUtils.getSharedPrefs(getApplicationContext())); + code = mTransProxy.setTransparentProxyingAll(this, true, shell); + } + else + { + ArrayList apps = AppManager.getApps(this, TorServiceUtils.getSharedPrefs(getApplicationContext())); - code = mTransProxy.setTransparentProxyingByApp(this,apps, true, shell); - } - - debug ("TorTransProxy resp code: " + code); - - if (code == 0) - { + code = mTransProxy.setTransparentProxyingByApp(this,apps, true, shell); + } + + debug ("TorTransProxy resp code: " + code); + + if (code == 0) + { - if (mTransProxyTethering) - { - showToolbarNotification(getString(R.string.transproxy_enabled_for_tethering_), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); + if (mTransProxyTethering) + { + showToolbarNotification(getString(R.string.transproxy_enabled_for_tethering_), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); - mTransProxy.enableTetheringRules(this, Shell.startRootShell()); - - } - else - { - showToolbarNotification(getString(R.string.transparent_proxying_enabled), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); + mTransProxy.enableTetheringRules(this, Shell.startRootShell()); + + } + else + { + showToolbarNotification(getString(R.string.transparent_proxying_enabled), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); - } - } - else - { - showToolbarNotification(getString(R.string.warning_error_starting_transparent_proxying_), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); + } + } + else + { + showToolbarNotification(getString(R.string.warning_error_starting_transparent_proxying_), TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); - } - - return true; - } + } + + return true; + } /* * activate means whether to apply the users preferences @@ -947,946 +916,923 @@ public class TorService extends Service implements TorServiceConstants, TorConst * the idea is that if Tor is off then transproxy is off */ private boolean disableTransparentProxy (Shell shell) throws Exception - { - - debug ("Transparent Proxying: disabling..."); + { + + debug ("Transparent Proxying: disabling..."); - if (mTransProxy == null) - mTransProxy = new TorTransProxy(this, fileXtables); + if (mTransProxy == null) + mTransProxy = new TorTransProxy(this, fileXtables); - mTransProxy.setTransparentProxyingAll(this, false, shell); - ArrayList apps = AppManager.getApps(this, TorServiceUtils.getSharedPrefs(getApplicationContext())); - mTransProxy.setTransparentProxyingByApp(this, apps, false, shell); - - return true; - } + mTransProxy.setTransparentProxyingAll(this, false, shell); + ArrayList apps = AppManager.getApps(this, TorServiceUtils.getSharedPrefs(getApplicationContext())); + mTransProxy.setTransparentProxyingByApp(this, apps, false, shell); + + return true; + } private boolean runTorShellCmd(Shell shell) throws Exception { - String torrcPath = new File(appBinHome, TORRC_ASSET_KEY).getCanonicalPath(); + String torrcPath = new File(appBinHome, TORRC_ASSET_KEY).getCanonicalPath(); - updateTorConfigFile(); - - sendCallbackLogMessage(getString(R.string.status_starting_up)); - - String torCmdString = fileTor.getCanonicalPath() - + " DataDirectory " + appCacheHome.getCanonicalPath() - + " --defaults-torrc " + torrcPath - + " -f " + torrcPath + ".custom"; - - debug(torCmdString); - - SimpleCommand shellTorCommand = new SimpleCommand(torCmdString + " --verify-config"); - shell.add(shellTorCommand).waitForFinish(); - - int exitCode = shellTorCommand.getExitCode(); - String output = shellTorCommand.getOutput(); - - if (exitCode != 0 && output != null && output.length() > 0) - { - logNotice("Tor (" + exitCode + "): " + output); - throw new Exception ("Torrc config did not verify"); - - } + updateTorConfigFile(); + + sendCallbackLogMessage(getString(R.string.status_starting_up)); + + String torCmdString = fileTor.getCanonicalPath() + + " DataDirectory " + appCacheHome.getCanonicalPath() + + " --defaults-torrc " + torrcPath + + " -f " + torrcPath + ".custom"; + + debug(torCmdString); + + SimpleCommand shellTorCommand = new SimpleCommand(torCmdString + " --verify-config"); + shell.add(shellTorCommand).waitForFinish(); + + int exitCode = shellTorCommand.getExitCode(); + String output = shellTorCommand.getOutput(); + + if (exitCode != 0 && output != null && output.length() > 0) + { + logNotice("Tor (" + exitCode + "): " + output); + throw new Exception ("Torrc config did not verify"); + + } - shellTorCommand = new SimpleCommand(torCmdString); - shell.add(shellTorCommand).waitForFinish(); - - exitCode = shellTorCommand.getExitCode(); - output = shellTorCommand.getOutput(); - + shellTorCommand = new SimpleCommand(torCmdString); + shell.add(shellTorCommand).waitForFinish(); + + exitCode = shellTorCommand.getExitCode(); + output = shellTorCommand.getOutput(); + - if (exitCode != 0 && output != null && output.length() > 0) - { - logNotice("Tor (" + exitCode + "): " + output); - //throw new Exception ("unable to start"); - return false; - } + if (exitCode != 0 && output != null && output.length() > 0) + { + logNotice("Tor (" + exitCode + "): " + output); + //throw new Exception ("unable to start"); + return false; + } - //now try to connect - mLastProcessId = initControlConnection (100,false); + //now try to connect + mLastProcessId = initControlConnection (100,false); - if (mLastProcessId == -1) - { - logNotice(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + shellTorCommand.getExitCode() + ": " + shellTorCommand.getOutput()); - sendCallbackLogMessage(getString(R.string.couldn_t_start_tor_process_)); - - throw new Exception ("Unable to start Tor"); - } - else - { - - logNotice("Tor started; process id=" + mLastProcessId); - - processSettingsImpl(); - + if (mLastProcessId == -1) + { + logNotice(getString(R.string.couldn_t_start_tor_process_) + "; exit=" + shellTorCommand.getExitCode() + ": " + shellTorCommand.getOutput()); + sendCallbackLogMessage(getString(R.string.couldn_t_start_tor_process_)); + + throw new Exception ("Unable to start Tor"); + } + else + { + + logNotice("Tor started; process id=" + mLastProcessId); + + processSettingsImpl(); + - } - - return true; + } + + return true; } private void updatePolipoConfig () throws FileNotFoundException, IOException { - + - File file = new File(appBinHome, POLIPOCONFIG_ASSET_KEY); - - Properties props = new Properties(); - - props.load(new FileReader(file)); - - props.put("socksParentProxy", "\"localhost:" + mPortSOCKS + "\""); - props.put("proxyPort",mPortHTTP+""); - - props.store(new FileWriter(file), "updated"); - + File file = new File(appBinHome, POLIPOCONFIG_ASSET_KEY); + + Properties props = new Properties(); + + props.load(new FileReader(file)); + + props.put("socksParentProxy", "\"localhost:" + mPortSOCKS + "\""); + props.put("proxyPort",mPortHTTP+""); + + props.store(new FileWriter(file), "updated"); + } private void runPolipoShellCmd (Shell shell) throws Exception { - - logNotice( "Starting polipo process"); - - int polipoProcId = TorServiceUtils.findProcessId(filePolipo.getCanonicalPath()); + + logNotice( "Starting polipo process"); + + int polipoProcId = TorServiceUtils.findProcessId(filePolipo.getCanonicalPath()); - StringBuilder log = null; - - int attempts = 0; - - if (polipoProcId == -1) - { - log = new StringBuilder(); - - updatePolipoConfig(); - - String polipoConfigPath = new File(appBinHome, POLIPOCONFIG_ASSET_KEY).getCanonicalPath(); - SimpleCommand cmdPolipo = new SimpleCommand(filePolipo.getCanonicalPath() + " -c " + polipoConfigPath + " &"); - - shell.add(cmdPolipo); - - //wait one second to make sure it has started up - Thread.sleep(1000); - - while ((polipoProcId = TorServiceUtils.findProcessId(filePolipo.getCanonicalPath())) == -1 && attempts < MAX_START_TRIES) - { - logNotice("Couldn't find Polipo process... retrying...\n" + log); - Thread.sleep(3000); - attempts++; - } - - logNotice(log.toString()); - } - - sendCallbackLogMessage(getString(R.string.privoxy_is_running_on_port_) + PORT_HTTP); - - logNotice("Polipo process id=" + polipoProcId); - + StringBuilder log = null; + + int attempts = 0; + + if (polipoProcId == -1) + { + log = new StringBuilder(); + + updatePolipoConfig(); + + String polipoConfigPath = new File(appBinHome, POLIPOCONFIG_ASSET_KEY).getCanonicalPath(); + SimpleCommand cmdPolipo = new SimpleCommand(filePolipo.getCanonicalPath() + " -c " + polipoConfigPath + " &"); + + shell.add(cmdPolipo); + + //wait one second to make sure it has started up + Thread.sleep(1000); + + while ((polipoProcId = TorServiceUtils.findProcessId(filePolipo.getCanonicalPath())) == -1 && attempts < MAX_START_TRIES) + { + logNotice("Couldn't find Polipo process... retrying...\n" + log); + Thread.sleep(3000); + attempts++; + } + + logNotice(log.toString()); + } + + sendCallbackLogMessage(getString(R.string.privoxy_is_running_on_port_) + PORT_HTTP); + + logNotice("Polipo process id=" + polipoProcId); + } - private int initControlConnection (int maxTries, boolean isReconnect) throws Exception, RuntimeException - { - int i = 0; - int controlPort = -1; - - int attempt = 0; - + private int initControlConnection (int maxTries, boolean isReconnect) throws Exception, RuntimeException + { + int i = 0; + int controlPort = -1; + + int attempt = 0; + - logNotice( "Waiting for control port..."); - - while (conn == null && attempt++ < maxTries) - { - try - { - - controlPort = getControlPort(); - - if (controlPort != -1) - { - logNotice( "Connecting to control port: " + controlPort); - - torConnSocket = new Socket(IP_LOCALHOST, controlPort); - torConnSocket.setSoTimeout(CONTROL_SOCKET_TIMEOUT); - - conn = new TorControlConnection(torConnSocket); - conn.launchThread(true);//is daemon - - break; - } - - } - catch (Exception ce) - { - conn = null; - // logException( "Error connecting to Tor local control port: " + ce.getMessage(),ce); - - } - - - try { - // logNotice("waiting..."); - Thread.sleep(1000); } - catch (Exception e){} - - - - } - - if (conn != null) - { - logNotice( "SUCCESS connected to Tor control port."); - - File fileCookie = new File(appCacheHome, TOR_CONTROL_COOKIE); - - if (fileCookie.exists()) - { - byte[] cookie = new byte[(int)fileCookie.length()]; - DataInputStream fis = new DataInputStream(new FileInputStream(fileCookie)); - fis.read(cookie); - fis.close(); - conn.authenticate(cookie); - - logNotice( "SUCCESS - authenticated to control port."); - - sendCallbackLogMessage(getString(R.string.tor_process_starting) + ' ' + getString(R.string.tor_process_complete)); - - addEventHandler(); - - String torProcId = conn.getInfo("process/pid"); - - //remove this for now until we can make a clean way to share logs from internal storage - /** - if (ENABLE_DEBUG_LOG) - { - File fileLog2 = new File(getFilesDir(),"orbot-tor-log.txt"); - fileLog2.setReadable(true); - conn.setConf("Log", "debug file " + fileLog2.getCanonicalPath()); - }*/ - - mCurrentStatus = STATUS_CONNECTING; - sendCallbackStatus(mCurrentStatus); - - String confSocks = conn.getInfo("net/listeners/socks"); - StringTokenizer st = new StringTokenizer(confSocks," "); + logNotice( "Waiting for control port..."); + + while (conn == null && attempt++ < maxTries) + { + try + { + + controlPort = getControlPort(); + + if (controlPort != -1) + { + logNotice( "Connecting to control port: " + controlPort); + + torConnSocket = new Socket(IP_LOCALHOST, controlPort); + torConnSocket.setSoTimeout(CONTROL_SOCKET_TIMEOUT); + + conn = new TorControlConnection(torConnSocket); + conn.launchThread(true);//is daemon + + break; + } + + } + catch (Exception ce) + { + conn = null; + // logException( "Error connecting to Tor local control port: " + ce.getMessage(),ce); + + } + + + try { + // logNotice("waiting..."); + Thread.sleep(1000); } + catch (Exception e){} + + + + } + + if (conn != null) + { + logNotice( "SUCCESS connected to Tor control port."); + + File fileCookie = new File(appCacheHome, TOR_CONTROL_COOKIE); + + if (fileCookie.exists()) + { + byte[] cookie = new byte[(int)fileCookie.length()]; + DataInputStream fis = new DataInputStream(new FileInputStream(fileCookie)); + fis.read(cookie); + fis.close(); + conn.authenticate(cookie); + + logNotice( "SUCCESS - authenticated to control port."); + + sendCallbackLogMessage(getString(R.string.tor_process_starting) + ' ' + getString(R.string.tor_process_complete)); + + addEventHandler(); + + String torProcId = conn.getInfo("process/pid"); + + //remove this for now until we can make a clean way to share logs from internal storage + /** + if (ENABLE_DEBUG_LOG) + { + File fileLog2 = new File(getFilesDir(),"orbot-tor-log.txt"); + fileLog2.setReadable(true); + conn.setConf("Log", "debug file " + fileLog2.getCanonicalPath()); + }*/ + + mCurrentStatus = STATUS_CONNECTING; + sendCallbackStatus(mCurrentStatus); + + String confSocks = conn.getInfo("net/listeners/socks"); + StringTokenizer st = new StringTokenizer(confSocks," "); - confSocks = st.nextToken().split(":")[1]; - confSocks = confSocks.substring(0,confSocks.length()-1); - mPortSOCKS = Integer.parseInt(confSocks); - - if (!isReconnect) //if we are reconnected then we don't need to reset the ports - { + confSocks = st.nextToken().split(":")[1]; + confSocks = confSocks.substring(0,confSocks.length()-1); + mPortSOCKS = Integer.parseInt(confSocks); + + if (!isReconnect) //if we are reconnected then we don't need to reset the ports + { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - - String socksPortPref = prefs.getString(TorConstants.PREF_SOCKS, TorServiceConstants.PORT_SOCKS_DEFAULT); - if (socksPortPref.indexOf(':')!=-1) - socksPortPref = socksPortPref.split(":")[1]; - - String transPort = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); - if (transPort.indexOf(':')!=-1) - transPort = transPort.split(":")[1]; - - String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_DNS_PORT_DEFAULT+""); - if (dnsPort.indexOf(':')!=-1) - dnsPort = dnsPort.split(":")[1]; - - try - { - int newSocksPort = Integer.parseInt(socksPortPref); - ServerSocket ss = new ServerSocket(newSocksPort); - ss.close(); - - ArrayList socksLines = new ArrayList(); - socksLines.add("SOCKSPort " + mPortSOCKS); - socksLines.add("SOCKSPort " + socksPortPref); - - conn.setConf(socksLines); - - mPortSOCKS = newSocksPort; - - sendCallbackLogMessage("Local SOCKS port: " + socksPortPref); + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + + String socksPortPref = prefs.getString(TorConstants.PREF_SOCKS, TorServiceConstants.PORT_SOCKS_DEFAULT); + if (socksPortPref.indexOf(':')!=-1) + socksPortPref = socksPortPref.split(":")[1]; + + String transPort = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); + if (transPort.indexOf(':')!=-1) + transPort = transPort.split(":")[1]; + + String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_DNS_PORT_DEFAULT+""); + if (dnsPort.indexOf(':')!=-1) + dnsPort = dnsPort.split(":")[1]; + + try + { + int newSocksPort = Integer.parseInt(socksPortPref); + ServerSocket ss = new ServerSocket(newSocksPort); + ss.close(); + + ArrayList socksLines = new ArrayList(); + socksLines.add("SOCKSPort " + mPortSOCKS); + socksLines.add("SOCKSPort " + socksPortPref); + + conn.setConf(socksLines); + + mPortSOCKS = newSocksPort; + + sendCallbackLogMessage("Local SOCKS port: " + socksPortPref); - } - catch (Exception e) - { - sendCallbackLogMessage("Error setting TransProxy port to: " + socksPortPref); + } + catch (Exception e) + { + sendCallbackLogMessage("Error setting TransProxy port to: " + socksPortPref); - - } - - try - { - int newPort = Integer.parseInt(transPort); - ServerSocket ss = new ServerSocket(newPort); - ss.close(); - - ArrayList confLines = new ArrayList(); - - confLines.add("TransPort " + transPort); - - conn.setConf(confLines); - - sendCallbackLogMessage("Local TransProxy port: " + transPort); + + } + + try + { + int newPort = Integer.parseInt(transPort); + ServerSocket ss = new ServerSocket(newPort); + ss.close(); + + ArrayList confLines = new ArrayList(); + + confLines.add("TransPort " + transPort); + + conn.setConf(confLines); + + sendCallbackLogMessage("Local TransProxy port: " + transPort); - } - catch (Exception e) - { - sendCallbackLogMessage("ERROR setting TransProxy port to: " + transPort); + } + catch (Exception e) + { + sendCallbackLogMessage("ERROR setting TransProxy port to: " + transPort); - + - } - - try - { - int newPort = Integer.parseInt(dnsPort); - ServerSocket ss = new ServerSocket(newPort); - ss.close(); - - ArrayList confLines = new ArrayList(); - - confLines.add("DNSPort " + dnsPort); - - conn.setConf(confLines); - - sendCallbackLogMessage("Local DNSPort port: " + transPort); + } + + try + { + int newPort = Integer.parseInt(dnsPort); + ServerSocket ss = new ServerSocket(newPort); + ss.close(); + + ArrayList confLines = new ArrayList(); + + confLines.add("DNSPort " + dnsPort); + + conn.setConf(confLines); + + sendCallbackLogMessage("Local DNSPort port: " + transPort); - } - catch (Exception e) - { - sendCallbackLogMessage("ERROR setting DNSport to: " + dnsPort); - + } + catch (Exception e) + { + sendCallbackLogMessage("ERROR setting DNSport to: " + dnsPort); + - } - } - - return Integer.parseInt(torProcId); - - } - else - { - logNotice ("Tor authentication cookie does not exist yet"); - conn = null; - - } - } - - - return -1; + } + } + + return Integer.parseInt(torProcId); + + } + else + { + logNotice ("Tor authentication cookie does not exist yet"); + conn = null; + + } + } + + + return -1; - } - - private int getControlPort () - { - int result = -1; - - try - { - if (fileControlPort.exists()) - { - debug("Reading control port config file: " + fileControlPort.getCanonicalPath()); - BufferedReader bufferedReader = new BufferedReader(new FileReader(fileControlPort)); - String line = bufferedReader.readLine(); - - if (line != null) - { - String[] lineParts = line.split(":"); - result = Integer.parseInt(lineParts[1]); - } - + } + + private int getControlPort () + { + int result = -1; + + try + { + if (fileControlPort.exists()) + { + debug("Reading control port config file: " + fileControlPort.getCanonicalPath()); + BufferedReader bufferedReader = new BufferedReader(new FileReader(fileControlPort)); + String line = bufferedReader.readLine(); + + if (line != null) + { + String[] lineParts = line.split(":"); + result = Integer.parseInt(lineParts[1]); + } + - bufferedReader.close(); + bufferedReader.close(); - //store last valid control port - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - prefs.edit().putInt("controlport", result).commit(); - - } - else - { - debug("Control Port config file does not yet exist (waiting for tor): " + fileControlPort.getCanonicalPath()); - - } - - - } - catch (FileNotFoundException e) - { - debug("unable to get control port; file not found"); - } - catch (Exception e) - { - debug("unable to read control port config file"); - } + //store last valid control port + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + prefs.edit().putInt("controlport", result).commit(); + + } + else + { + debug("Control Port config file does not yet exist (waiting for tor): " + fileControlPort.getCanonicalPath()); + + } + + + } + catch (FileNotFoundException e) + { + debug("unable to get control port; file not found"); + } + catch (Exception e) + { + debug("unable to read control port config file"); + } - return result; - } - - /* - private void getTorStatus () throws IOException - { - try - { - - if (conn != null) - { - // get a single value. - - // get several values - - if (mCurrentStatus == STATUS_CONNECTING) - { - //Map vals = conn.getInfo(Arrays.asList(new String[]{ - // "status/bootstrap-phase", "status","version"})); - - String bsPhase = conn.getInfo("status/bootstrap-phase"); - Log.d(TAG, "bootstrap-phase: " + bsPhase); - - - } - else - { - // String status = conn.getInfo("status/circuit-established"); - // Log.d(TAG, "status/circuit-established=" + status); - } - } - } - catch (Exception e) - { - Log.d(TAG, "Unable to get Tor status from control port"); - mCurrentStatus = STATUS_UNAVAILABLE; - } - - }*/ - - - public void addEventHandler () throws Exception - { - // We extend NullEventHandler so that we don't need to provide empty - // implementations for all the events we don't care about. - // ... - logNotice( "adding control port event handler"); + return result; + } + + /* + private void getTorStatus () throws IOException + { + try + { + + if (conn != null) + { + // get a single value. + + // get several values + + if (mCurrentStatus == STATUS_CONNECTING) + { + //Map vals = conn.getInfo(Arrays.asList(new String[]{ + // "status/bootstrap-phase", "status","version"})); + + String bsPhase = conn.getInfo("status/bootstrap-phase"); + Log.d(TAG, "bootstrap-phase: " + bsPhase); + + + } + else + { + // String status = conn.getInfo("status/circuit-established"); + // Log.d(TAG, "status/circuit-established=" + status); + } + } + } + catch (Exception e) + { + Log.d(TAG, "Unable to get Tor status from control port"); + mCurrentStatus = STATUS_UNAVAILABLE; + } + + }*/ + + + public void addEventHandler () throws Exception + { + // We extend NullEventHandler so that we don't need to provide empty + // implementations for all the events we don't care about. + // ... + logNotice( "adding control port event handler"); - conn.setEventHandler(this); - - conn.setEvents(Arrays.asList(new String[]{ - "ORCONN", "CIRC", "NOTICE", "WARN", "ERR","BW"})); - // conn.setEvents(Arrays.asList(new String[]{ - // "DEBUG", "INFO", "NOTICE", "WARN", "ERR"})); + conn.setEventHandler(this); + + conn.setEvents(Arrays.asList(new String[]{ + "ORCONN", "CIRC", "NOTICE", "WARN", "ERR","BW"})); + // conn.setEvents(Arrays.asList(new String[]{ + // "DEBUG", "INFO", "NOTICE", "WARN", "ERR"})); - logNotice( "SUCCESS added control port event handler"); - - + logNotice( "SUCCESS added control port event handler"); + + - } - - /** - * Returns the port number that the HTTP proxy is running on - */ - public int getHTTPPort() throws RemoteException { - return mPortHTTP; - } + } + + /** + * Returns the port number that the HTTP proxy is running on + */ + public int getHTTPPort() throws RemoteException { + return mPortHTTP; + } - - /** - * Returns the port number that the HTTP proxy is running on - */ - public int getSOCKSPort() throws RemoteException { - return mPortSOCKS; - } + + /** + * Returns the port number that the HTTP proxy is running on + */ + public int getSOCKSPort() throws RemoteException { + return mPortSOCKS; + } - public void setTorProfile(int newState) { - - if (newState == STATUS_ON) - { - - if (mCurrentStatus == STATUS_OFF) - { - sendCallbackLogMessage (getString(R.string.status_starting_up)); - - - try - { + public void setTorProfile(int newState) { + + if (newState == STATUS_ON) + { + + if (mCurrentStatus == STATUS_OFF) + { + sendCallbackLogMessage (getString(R.string.status_starting_up)); + + + try + { - boolean found = findExistingProc (); - - if (!found) - { - killProcess(fileTor); - killProcess(filePolipo); - - startTor(); - } - } - catch (Exception e) - { - - logException("Unable to start Tor: " + e.toString(),e); - mCurrentStatus = STATUS_OFF; - sendCallbackStatus(mCurrentStatus); - - showToolbarNotification(getString(R.string.unable_to_start_tor) + ": " + e.getMessage(), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); - stopTor(); - } - - } - } - else if (newState == STATUS_OFF) - { - if (mCurrentStatus == STATUS_ON) - { - sendCallbackLogMessage (getString(R.string.status_shutting_down)); - - stopTor(); - - mCurrentStatus = STATUS_OFF; - sendCallbackStatus(mCurrentStatus); - } - - } - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - public void startVpnService () { - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - Editor ePrefs = prefs.edit(); - - ePrefs.putString("pref_proxy_type", "socks5"); - ePrefs.putString("pref_proxy_host", "127.0.0.1"); - ePrefs.putString("pref_proxy_port", "9999"); - ePrefs.remove("pref_proxy_username"); - ePrefs.remove("pref_proxy_password"); - ePrefs.commit(); - processSettings(); - - Intent intent = new Intent(TorService.this, OrbotVpnService.class); + boolean found = findExistingProc (); + + if (!found) + { + killProcess(fileTor); + killProcess(filePolipo); + + startTor(); + } + } + catch (Exception e) + { + + logException("Unable to start Tor: " + e.toString(),e); + mCurrentStatus = STATUS_OFF; + sendCallbackStatus(mCurrentStatus); + + showToolbarNotification(getString(R.string.unable_to_start_tor) + ": " + e.getMessage(), ERROR_NOTIFY_ID, R.drawable.ic_stat_notifyerr); + stopTor(); + } + + } + } + else if (newState == STATUS_OFF) + { + if (mCurrentStatus == STATUS_ON) + { + sendCallbackLogMessage (getString(R.string.status_shutting_down)); + + stopTor(); + + mCurrentStatus = STATUS_OFF; + sendCallbackStatus(mCurrentStatus); + } + + } + } + + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + public void startVpnService () { + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + Editor ePrefs = prefs.edit(); + + ePrefs.putString("pref_proxy_type", "socks5"); + ePrefs.putString("pref_proxy_host", "127.0.0.1"); + ePrefs.putString("pref_proxy_port", "9999"); + ePrefs.remove("pref_proxy_username"); + ePrefs.remove("pref_proxy_password"); + ePrefs.commit(); + processSettings(); + + Intent intent = new Intent(TorService.this, OrbotVpnService.class); startService(intent); - - } - - public void stopVpnService () - { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - Editor ePrefs = prefs.edit(); - - ePrefs.remove("pref_proxy_type"); - ePrefs.remove("pref_proxy_host"); - ePrefs.remove("pref_proxy_port"); - ePrefs.remove("pref_proxy_username"); - ePrefs.remove("pref_proxy_password"); - ePrefs.commit(); - processSettings(); - } + + } + + public void stopVpnService () + { + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + Editor ePrefs = prefs.edit(); + + ePrefs.remove("pref_proxy_type"); + ePrefs.remove("pref_proxy_host"); + ePrefs.remove("pref_proxy_port"); + ePrefs.remove("pref_proxy_username"); + ePrefs.remove("pref_proxy_password"); + ePrefs.commit(); + processSettings(); + } - + - public void message(String severity, String msg) { - - - logNotice(severity + ": " + msg); + public void message(String severity, String msg) { + + + logNotice(severity + ": " + msg); if (msg.indexOf(TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)!=-1) { - mCurrentStatus = STATUS_ON; - sendCallbackStatus(mCurrentStatus); - + mCurrentStatus = STATUS_ON; + sendCallbackStatus(mCurrentStatus); + - showToolbarNotification(getString(R.string.status_activated), NOTIFY_ID, R.drawable.ic_stat_tor); + showToolbarNotification(getString(R.string.status_activated), NOTIFY_ID, R.drawable.ic_stat_tor); } - - } + + } - public void newDescriptors(List orList) { - - } - - - public void orConnStatus(String status, String orName) { - - - StringBuilder sb = new StringBuilder(); - sb.append("orConnStatus ("); - sb.append(parseNodeName(orName) ); - sb.append("): "); - sb.append(status); - - debug(sb.toString()); - - } - - - public void streamStatus(String status, String streamID, String target) { - - StringBuilder sb = new StringBuilder(); - sb.append("StreamStatus ("); - sb.append((streamID)); - sb.append("): "); - sb.append(status); - - logNotice(sb.toString()); - - } - - - public void unrecognized(String type, String msg) { - - StringBuilder sb = new StringBuilder(); - sb.append("Message ("); - sb.append(type); - sb.append("): "); - sb.append(msg); - - logNotice(sb.toString()); - - - } - - public void bandwidthUsed(long read, long written) { - - if (read != lastRead || written != lastWritten) - { - StringBuilder sb = new StringBuilder(); - sb.append(formatCount(read)); - sb.append(" \u2193"); - sb.append(" / "); - sb.append(formatCount(written)); - sb.append(" \u2191"); - - int iconId = R.drawable.ic_stat_tor; - - if (read > 0 || written > 0) - iconId = R.drawable.ic_stat_tor_xfer; - - if (mConnectivity && prefPersistNotifications) - showToolbarNotification(sb.toString(), NOTIFY_ID, iconId); - - mTotalTrafficWritten += written; - mTotalTrafficRead += read; - - - } - - lastWritten = written; - lastRead = read; - - sendCallbackStatusMessage(lastWritten, lastRead, mTotalTrafficWritten, mTotalTrafficRead); - - } - - private String formatCount(long count) { - // Converts the supplied argument into a string. - // Under 2Mb, returns "xxx.xKb" - // Over 2Mb, returns "xxx.xxMb" - if (count < 1e6) - return ((float)((int)(count*10/1024))/10 + "Kbps"); - return ((float)((int)(count*100/1024/1024))/100 + "Mbps"); - - //return count+" kB"; - } - - public void circuitStatus(String status, String circID, String path) { - - - StringBuilder sb = new StringBuilder(); - sb.append("Circuit ("); - sb.append((circID)); - sb.append(") "); - sb.append(status); - sb.append(": "); - - StringTokenizer st = new StringTokenizer(path,","); - Node node = null; - - while (st.hasMoreTokens()) - { - String nodePath = st.nextToken(); - node = new Node(); - - String[] nodeParts; - - if (nodePath.contains("=")) - nodeParts = nodePath.split("="); - else - nodeParts = nodePath.split("~"); - - if (nodeParts.length == 1) - { - node.id = nodeParts[0].substring(1); - node.name = node.id; - } - else if (nodeParts.length == 2) - { - node.id = nodeParts[0].substring(1); - node.name = nodeParts[1]; - } - - node.status = status; - - sb.append(node.name); - - if (st.hasMoreTokens()) - sb.append (" > "); - } - - if (ENABLE_DEBUG_LOG) - debug(sb.toString()); - else if(status.equals("BUILT")) - { - - if (mCurrentStatus == STATUS_CONNECTING) - mCurrentStatus = STATUS_ON; - - sendCallbackStatus(mCurrentStatus); - - - logNotice(sb.toString()); - - } - else if (status.equals("CLOSED")) - { - logNotice(sb.toString()); - - } - - if (mShowExpandedNotifications) - { - //get IP from last nodename - if(status.equals("BUILT")){ - - if (node.ipAddress == null) - mExecutor.execute(new ExternalIPFetcher(node)); - - hmBuiltNodes.put(node.id, node); - } - - if (status.equals("CLOSED")) - { - hmBuiltNodes.remove(node.id); - - } - } - - } - - private HashMap hmBuiltNodes = new HashMap(); - - class Node - { - String status; - String id; - String name; - String ipAddress; - String country; - String organization; - } - - private class ExternalIPFetcher implements Runnable { - - private Node mNode; - private int MAX_ATTEMPTS = 3; - private final static String ONIONOO_BASE_URL = "https://onionoo.torproject.org/details?fields=country_name,as_name,or_addresses&lookup="; - - public ExternalIPFetcher (Node node) - { - mNode = node; - } - - public void run () - { - - for (int i = 0; i < MAX_ATTEMPTS; i++) - { - if (conn != null) - { - try { - - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8118)); - - URLConnection conn = new URL(ONIONOO_BASE_URL + mNode.id).openConnection(proxy); - conn.setRequestProperty("Connection","Close"); - conn.setConnectTimeout(60000); - conn.setReadTimeout(60000); - - InputStream is = conn.getInputStream(); - - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - // getting JSON string from URL - - StringBuffer json = new StringBuffer(); - String line = null; - - while ((line = reader.readLine())!=null) - json.append(line); - - JSONObject jsonNodeInfo = new org.json.JSONObject(json.toString()); - - JSONArray jsonRelays = jsonNodeInfo.getJSONArray("relays"); - - if (jsonRelays.length() > 0) - { - mNode.ipAddress = jsonRelays.getJSONObject(0).getJSONArray("or_addresses").getString(0).split(":")[0]; - mNode.country = jsonRelays.getJSONObject(0).getString("country_name"); - mNode.organization = jsonRelays.getJSONObject(0).getString("as_name"); - - StringBuffer sbInfo = new StringBuffer(); - sbInfo.append(mNode.ipAddress); - - if (mNode.country != null) - sbInfo.append(' ').append(mNode.country); - - if (mNode.organization != null) - sbInfo.append(" (").append(mNode.organization).append(')'); - - logNotice(sbInfo.toString()); - - } - - reader.close(); - is.close(); - - break; - - } catch (Exception e) { - - debug ("Error getting node details from onionoo: " + e.getMessage()); - - - } - } - } - } - - - } - - private String parseNodeName(String node) - { - if (node.indexOf('=')!=-1) - { - return (node.substring(node.indexOf("=")+1)); - } - else if (node.indexOf('~')!=-1) - { - return (node.substring(node.indexOf("~")+1)); - } - else - return node; - } - - + public void newDescriptors(List orList) { - public void processSettings () + } + + + public void orConnStatus(String status, String orName) { + + + StringBuilder sb = new StringBuilder(); + sb.append("orConnStatus ("); + sb.append(parseNodeName(orName) ); + sb.append("): "); + sb.append(status); + + debug(sb.toString()); + + } + + + public void streamStatus(String status, String streamID, String target) { + + StringBuilder sb = new StringBuilder(); + sb.append("StreamStatus ("); + sb.append((streamID)); + sb.append("): "); + sb.append(status); + + logNotice(sb.toString()); + + } + + + public void unrecognized(String type, String msg) { + + StringBuilder sb = new StringBuilder(); + sb.append("Message ("); + sb.append(type); + sb.append("): "); + sb.append(msg); + + logNotice(sb.toString()); + + + } + + public void bandwidthUsed(long read, long written) { + + if (read != lastRead || written != lastWritten) { - - try { - - boolean hadEnableTransparentProxy = mEnableTransparentProxy; - - updateSettings (); - - if (mHasRoot) - { - Shell shell = Shell.startRootShell(); + StringBuilder sb = new StringBuilder(); + sb.append(formatCount(read)); + sb.append(" \u2193"); + sb.append(" / "); + sb.append(formatCount(written)); + sb.append(" \u2191"); + + int iconId = R.drawable.ic_stat_tor; + + if (read > 0 || written > 0) + iconId = R.drawable.ic_stat_tor_xfer; + + if (mConnectivity && prefPersistNotifications) + showToolbarNotification(sb.toString(), NOTIFY_ID, iconId); - if (hadEnableTransparentProxy) - disableTransparentProxy(shell); + mTotalTrafficWritten += written; + mTotalTrafficRead += read; + - if (mEnableTransparentProxy) - enableTransparentProxy(shell); - - shell.close(); - } - - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - Thread thread = new Thread() - { - - public void run () - { - try { - - - - processSettingsImpl (); - - - } catch (Exception e) { - logException ("error applying mPrefs",e); - } - } - }; - - thread.start(); - } - + + lastWritten = written; + lastRead = read; + + sendCallbackStatusMessage(lastWritten, lastRead, mTotalTrafficWritten, mTotalTrafficRead); - public String getInfo (String key) { - try - { - if(conn !=null) - { - String m = conn.getInfo(key); - return m; - - } - } - catch(Exception ioe) - { - // Log.e(TAG,"Unable to get Tor information",ioe); - logNotice("Unable to get Tor information"+ioe.getMessage()); - } - return null; + } + + private String formatCount(long count) { + // Converts the supplied argument into a string. + // Under 2Mb, returns "xxx.xKb" + // Over 2Mb, returns "xxx.xxMb" + if (count < 1e6) + return ((float)((int)(count*10/1024))/10 + "Kbps"); + return ((float)((int)(count*100/1024/1024))/100 + "Mbps"); + + //return count+" kB"; + } + + public void circuitStatus(String status, String circID, String path) { + + + StringBuilder sb = new StringBuilder(); + sb.append("Circuit ("); + sb.append((circID)); + sb.append(") "); + sb.append(status); + sb.append(": "); + + StringTokenizer st = new StringTokenizer(path,","); + Node node = null; + + while (st.hasMoreTokens()) + { + String nodePath = st.nextToken(); + node = new Node(); + + String[] nodeParts; + + if (nodePath.contains("=")) + nodeParts = nodePath.split("="); + else + nodeParts = nodePath.split("~"); + + if (nodeParts.length == 1) + { + node.id = nodeParts[0].substring(1); + node.name = node.id; + } + else if (nodeParts.length == 2) + { + node.id = nodeParts[0].substring(1); + node.name = nodeParts[1]; + } + + node.status = status; + + sb.append(node.name); + + if (st.hasMoreTokens()) + sb.append (" > "); + } + + if (ENABLE_DEBUG_LOG) + debug(sb.toString()); + else if(status.equals("BUILT")) + { + + if (mCurrentStatus == STATUS_CONNECTING) + mCurrentStatus = STATUS_ON; + + sendCallbackStatus(mCurrentStatus); + + + logNotice(sb.toString()); + + } + else if (status.equals("CLOSED")) + { + logNotice(sb.toString()); + + } + + if (mShowExpandedNotifications) + { + //get IP from last nodename + if(status.equals("BUILT")){ + + if (node.ipAddress == null) + mExecutor.execute(new ExternalIPFetcher(node)); + + hmBuiltNodes.put(node.id, node); + } + + if (status.equals("CLOSED")) + { + hmBuiltNodes.remove(node.id); + + } + } + + } + + private HashMap hmBuiltNodes = new HashMap(); + + class Node + { + String status; + String id; + String name; + String ipAddress; + String country; + String organization; + } + + private class ExternalIPFetcher implements Runnable { + + private Node mNode; + private int MAX_ATTEMPTS = 3; + private final static String ONIONOO_BASE_URL = "https://onionoo.torproject.org/details?fields=country_name,as_name,or_addresses&lookup="; + + public ExternalIPFetcher (Node node) + { + mNode = node; + } + + public void run () + { + + for (int i = 0; i < MAX_ATTEMPTS; i++) + { + if (conn != null) + { + try { + + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8118)); + + URLConnection conn = new URL(ONIONOO_BASE_URL + mNode.id).openConnection(proxy); + conn.setRequestProperty("Connection","Close"); + conn.setConnectTimeout(60000); + conn.setReadTimeout(60000); + + InputStream is = conn.getInputStream(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + + // getting JSON string from URL + + StringBuffer json = new StringBuffer(); + String line = null; + + while ((line = reader.readLine())!=null) + json.append(line); + + JSONObject jsonNodeInfo = new org.json.JSONObject(json.toString()); + + JSONArray jsonRelays = jsonNodeInfo.getJSONArray("relays"); + + if (jsonRelays.length() > 0) + { + mNode.ipAddress = jsonRelays.getJSONObject(0).getJSONArray("or_addresses").getString(0).split(":")[0]; + mNode.country = jsonRelays.getJSONObject(0).getString("country_name"); + mNode.organization = jsonRelays.getJSONObject(0).getString("as_name"); + + StringBuffer sbInfo = new StringBuffer(); + sbInfo.append(mNode.ipAddress); + + if (mNode.country != null) + sbInfo.append(' ').append(mNode.country); + + if (mNode.organization != null) + sbInfo.append(" (").append(mNode.organization).append(')'); + + logNotice(sbInfo.toString()); + + } + + reader.close(); + is.close(); + + break; + + } catch (Exception e) { + + debug ("Error getting node details from onionoo: " + e.getMessage()); + + + } + } + } + } + + + } + + private String parseNodeName(String node) + { + if (node.indexOf('=')!=-1) + { + return (node.substring(node.indexOf("=")+1)); + } + else if (node.indexOf('~')!=-1) + { + return (node.substring(node.indexOf("~")+1)); + } + else + return node; + } + + + + public void processSettings (){ + try{ + boolean hadEnableTransparentProxy = mEnableTransparentProxy; + updateSettings (); + if (mHasRoot) + { + Shell shell = Shell.startRootShell(); + if (hadEnableTransparentProxy){ + disableTransparentProxy(shell); + } + if (mEnableTransparentProxy){ + enableTransparentProxy(shell); + } + shell.close(); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + Thread thread = new Thread(){ + public void run (){ + try { + processSettingsImpl(); + } catch (Exception e) { + logException ("error applying mPrefs",e); + } + } + }; + thread.start(); + } + + public String getInfo (String key) { + try { + if(conn !=null){ + String m = conn.getInfo(key); + return m; + } + } + catch(Exception ioe){ + // Log.e(TAG,"Unable to get Tor information",ioe); + logNotice("Unable to get Tor information"+ioe.getMessage()); + } + return null; } public String getConfiguration (String name) { - try - { - if (conn != null) - { - StringBuffer result = new StringBuffer(); - - List listCe = conn.getConf(name); - - Iterator itCe = listCe.iterator(); - ConfigEntry ce = null; - - - - while (itCe.hasNext()) - { - ce = itCe.next(); - - result.append(ce.key); - result.append(' '); - result.append(ce.value); - result.append('\n'); - } - - return result.toString(); - } - } - catch (Exception ioe) - { - - logException("Unable to get Tor configuration: " + ioe.getMessage(),ioe); - } - - return null; + try + { + if (conn != null) + { + StringBuffer result = new StringBuffer(); + + List listCe = conn.getConf(name); + + Iterator itCe = listCe.iterator(); + ConfigEntry ce = null; + + + + while (itCe.hasNext()) + { + ce = itCe.next(); + + result.append(ce.key); + result.append(' '); + result.append(ce.value); + result.append('\n'); + } + + return result.toString(); + } + } + catch (Exception ioe) + { + + logException("Unable to get Tor configuration: " + ioe.getMessage(),ioe); + } + + return null; } private final static String RESET_STRING = "=\"\""; @@ -1897,123 +1843,123 @@ public class TorService extends Service implements TorServiceConstants, TorConst { - if (configBuffer == null) - configBuffer = new ArrayList(); - - if (resetBuffer == null) - resetBuffer = new ArrayList(); - - if (value == null || value.length() == 0) - { - resetBuffer.add(name + RESET_STRING); - - } - else - { - StringBuffer sbConf = new StringBuffer(); - sbConf.append(name); - sbConf.append(' '); - sbConf.append(value); - - configBuffer.add(sbConf.toString()); - } - - return false; + if (configBuffer == null) + configBuffer = new ArrayList(); + + if (resetBuffer == null) + resetBuffer = new ArrayList(); + + if (value == null || value.length() == 0) + { + resetBuffer.add(name + RESET_STRING); + + } + else + { + StringBuffer sbConf = new StringBuffer(); + sbConf.append(name); + sbConf.append(' '); + sbConf.append(value); + + configBuffer.add(sbConf.toString()); + } + + return false; } public void newIdentity () { - //it is possible to not have a connection yet, and someone might try to newnym - if (conn != null) - { - new Thread () - { - public void run () - { - try { - - conn.signal("NEWNYM"); - - } - catch (Exception ioe){ - debug("error requesting newnym: " + ioe.getLocalizedMessage()); - } - } - }.start(); - } + //it is possible to not have a connection yet, and someone might try to newnym + if (conn != null) + { + new Thread () + { + public void run () + { + try { + + conn.signal("NEWNYM"); + + } + catch (Exception ioe){ + debug("error requesting newnym: " + ioe.getLocalizedMessage()); + } + } + }.start(); + } } - public boolean saveConfiguration () - { - try - { - if (conn != null) - { - - if (resetBuffer != null && resetBuffer.size() > 0) - { - for (String value : configBuffer) - { - - debug("removing torrc conf: " + value); - - - } - - conn.resetConf(resetBuffer); - resetBuffer = null; - } - - if (configBuffer != null && configBuffer.size() > 0) - { - - for (String value : configBuffer) - { - - debug("Setting torrc conf: " + value); - - - } - - conn.setConf(configBuffer); - - configBuffer = null; - } - - // Flush the configuration to disk. - //this is doing bad things right now NF 22/07/10 - //conn.saveConf(); - - return true; - } - } - catch (Exception ioe) - { - - logException("Unable to update Tor configuration: " + ioe.getMessage(),ioe); + public boolean saveConfiguration () + { + try + { + if (conn != null) + { + + if (resetBuffer != null && resetBuffer.size() > 0) + { + for (String value : configBuffer) + { + + debug("removing torrc conf: " + value); + + + } + + conn.resetConf(resetBuffer); + resetBuffer = null; + } + + if (configBuffer != null && configBuffer.size() > 0) + { + + for (String value : configBuffer) + { + + debug("Setting torrc conf: " + value); + + + } + + conn.setConf(configBuffer); + + configBuffer = null; + } + + // Flush the configuration to disk. + //this is doing bad things right now NF 22/07/10 + //conn.saveConf(); + + return true; + } + } + catch (Exception ioe) + { + + logException("Unable to update Tor configuration: " + ioe.getMessage(),ioe); - } - - return false; - - } + } + + return false; + + } - - - + + + private void sendCallbackStatusMessage (long upload, long download, long written, long read) { - - - Intent intent = new Intent("log"); - // You can also include some extra data. - intent.putExtra("up",upload); - intent.putExtra("down",download); - intent.putExtra("written",written); - intent.putExtra("read",read); - - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + + + Intent intent = new Intent("log"); + // You can also include some extra data. + intent.putExtra("up",upload); + intent.putExtra("down",download); + intent.putExtra("written",written); + intent.putExtra("read",read); + + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @@ -2021,23 +1967,23 @@ public class TorService extends Service implements TorServiceConstants, TorConst private void sendCallbackLogMessage (String logMessage) { - - - Intent intent = new Intent("log"); - // You can also include some extra data. - intent.putExtra("log", logMessage); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + + + Intent intent = new Intent("log"); + // You can also include some extra data. + intent.putExtra("log", logMessage); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } private void sendCallbackStatus (int currentStatus) { - - - Intent intent = new Intent("status"); - // You can also include some extra data. - intent.putExtra("status", currentStatus); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + + + Intent intent = new Intent("status"); + // You can also include some extra data. + intent.putExtra("status", currentStatus); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @@ -2048,85 +1994,85 @@ public class TorService extends Service implements TorServiceConstants, TorConst */ private final BroadcastReceiver mNetworkStateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { + @Override + public void onReceive(Context context, Intent intent) { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - boolean doNetworKSleep = prefs.getBoolean(TorConstants.PREF_DISABLE_NETWORK, true); - - final ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo netInfo = cm.getActiveNetworkInfo(); + boolean doNetworKSleep = prefs.getBoolean(TorConstants.PREF_DISABLE_NETWORK, true); + + final ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo netInfo = cm.getActiveNetworkInfo(); - if(netInfo != null && netInfo.isConnected()) { - // WE ARE CONNECTED: DO SOMETHING - mConnectivity = true; - } - else { - // WE ARE NOT: DO SOMETHING ELSE - mConnectivity = false; - } - - if (doNetworKSleep) - { - try { - updateConfiguration("DisableNetwork", mConnectivity ? "0" : "1", false); - - if (mCurrentStatus != STATUS_OFF) - { - if (!mConnectivity) - { - logNotice(context.getString(R.string.no_network_connectivity_putting_tor_to_sleep_)); - showToolbarNotification(getString(R.string.no_internet_connection_tor),NOTIFY_ID,R.drawable.ic_stat_tor_off); - - } - else - { - logNotice(context.getString(R.string.network_connectivity_is_good_waking_tor_up_)); - showToolbarNotification(getString(R.string.status_activated),NOTIFY_ID,R.drawable.ic_stat_tor); + if(netInfo != null && netInfo.isConnected()) { + // WE ARE CONNECTED: DO SOMETHING + mConnectivity = true; + } + else { + // WE ARE NOT: DO SOMETHING ELSE + mConnectivity = false; + } + + if (doNetworKSleep) + { + try { + updateConfiguration("DisableNetwork", mConnectivity ? "0" : "1", false); + + if (mCurrentStatus != STATUS_OFF) + { + if (!mConnectivity) + { + logNotice(context.getString(R.string.no_network_connectivity_putting_tor_to_sleep_)); + showToolbarNotification(getString(R.string.no_internet_connection_tor),NOTIFY_ID,R.drawable.ic_stat_tor_off); + + } + else + { + logNotice(context.getString(R.string.network_connectivity_is_good_waking_tor_up_)); + showToolbarNotification(getString(R.string.status_activated),NOTIFY_ID,R.drawable.ic_stat_tor); - if (mHasRoot && mEnableTransparentProxy && mTransProxyNetworkRefresh) - { - - Shell shell = Shell.startRootShell(); - - disableTransparentProxy(shell); - enableTransparentProxy(shell); - - shell.close(); - } - - } - } - - } catch (Exception e) { - logException ("error updating state after network restart",e); - } - } - - } + if (mHasRoot && mEnableTransparentProxy && mTransProxyNetworkRefresh) + { + + Shell shell = Shell.startRootShell(); + + disableTransparentProxy(shell); + enableTransparentProxy(shell); + + shell.close(); + } + + } + } + + } catch (Exception e) { + logException ("error updating state after network restart",e); + } + } + + } }; private boolean processSettingsImpl () throws Exception { - logNotice(getString(R.string.updating_settings_in_tor_service)); - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + logNotice(getString(R.string.updating_settings_in_tor_service)); + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - /* + /* String socksConfig = prefs.getString(TorConstants.PREF_SOCKS, TorServiceConstants.PORT_SOCKS_DEFAULT); - enableSocks (socksConfig,false); - - String transPort = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); - String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_DNS_PORT_DEFAULT+""); - - enableTransProxyAndDNSPorts(transPort, dnsPort); - */ - - boolean useBridges = prefs.getBoolean(TorConstants.PREF_BRIDGES_ENABLED, false); - - //boolean autoUpdateBridges = prefs.getBoolean(TorConstants.PREF_BRIDGES_UPDATED, false); + enableSocks (socksConfig,false); + + String transPort = prefs.getString("pref_transport", TorServiceConstants.TOR_TRANSPROXY_PORT_DEFAULT+""); + String dnsPort = prefs.getString("pref_dnsport", TorServiceConstants.TOR_DNS_PORT_DEFAULT+""); + + enableTransProxyAndDNSPorts(transPort, dnsPort); + */ + + boolean useBridges = prefs.getBoolean(TorConstants.PREF_BRIDGES_ENABLED, false); + + //boolean autoUpdateBridges = prefs.getBoolean(TorConstants.PREF_BRIDGES_UPDATED, false); boolean becomeRelay = prefs.getBoolean(TorConstants.PREF_OR, false); boolean ReachableAddresses = prefs.getBoolean(TorConstants.PREF_REACHABLE_ADDRESSES,false); @@ -2140,133 +2086,133 @@ public class TorService extends Service implements TorServiceConstants, TorConst String proxyType = prefs.getString("pref_proxy_type", null); if (proxyType != null && proxyType.length() > 0) { - String proxyHost = prefs.getString("pref_proxy_host", null); - String proxyPort = prefs.getString("pref_proxy_port", null); - String proxyUser = prefs.getString("pref_proxy_username", null); - String proxyPass = prefs.getString("pref_proxy_password", null); - - if ((proxyHost != null && proxyHost.length()>0) && (proxyPort != null && proxyPort.length() > 0)) - { - updateConfiguration(proxyType + "Proxy", proxyHost + ':' + proxyPort, false); - - if (proxyUser != null && proxyPass != null) - { - if (proxyType.equalsIgnoreCase("socks5")) - { - updateConfiguration("Socks5ProxyUsername", proxyUser, false); - updateConfiguration("Socks5ProxyPassword", proxyPass, false); - } - else - updateConfiguration(proxyType + "ProxyAuthenticator", proxyUser + ':' + proxyPort, false); - - } - else if (proxyPass != null) - updateConfiguration(proxyType + "ProxyAuthenticator", proxyUser + ':' + proxyPort, false); - - + String proxyHost = prefs.getString("pref_proxy_host", null); + String proxyPort = prefs.getString("pref_proxy_port", null); + String proxyUser = prefs.getString("pref_proxy_username", null); + String proxyPass = prefs.getString("pref_proxy_password", null); + + if ((proxyHost != null && proxyHost.length()>0) && (proxyPort != null && proxyPort.length() > 0)) + { + updateConfiguration(proxyType + "Proxy", proxyHost + ':' + proxyPort, false); + + if (proxyUser != null && proxyPass != null) + { + if (proxyType.equalsIgnoreCase("socks5")) + { + updateConfiguration("Socks5ProxyUsername", proxyUser, false); + updateConfiguration("Socks5ProxyPassword", proxyPass, false); + } + else + updateConfiguration(proxyType + "ProxyAuthenticator", proxyUser + ':' + proxyPort, false); + + } + else if (proxyPass != null) + updateConfiguration(proxyType + "ProxyAuthenticator", proxyUser + ':' + proxyPort, false); + + - } + } } if (entranceNodes.length() > 0 || exitNodes.length() > 0 || excludeNodes.length() > 0) { - //only apply GeoIP if you need it - File fileGeoIP = new File(appBinHome,GEOIP_ASSET_KEY); - File fileGeoIP6 = new File(appBinHome,GEOIP6_ASSET_KEY); - - try - { - if ((!fileGeoIP.exists())) - { - TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); - boolean success = installer.installGeoIP(); - - } - - updateConfiguration("GeoIPFile", fileGeoIP.getCanonicalPath(), false); - updateConfiguration("GeoIPv6File", fileGeoIP6.getCanonicalPath(), false); + //only apply GeoIP if you need it + File fileGeoIP = new File(appBinHome,GEOIP_ASSET_KEY); + File fileGeoIP6 = new File(appBinHome,GEOIP6_ASSET_KEY); + + try + { + if ((!fileGeoIP.exists())) + { + TorResourceInstaller installer = new TorResourceInstaller(this, appBinHome); + boolean success = installer.installGeoIP(); + + } + + updateConfiguration("GeoIPFile", fileGeoIP.getCanonicalPath(), false); + updateConfiguration("GeoIPv6File", fileGeoIP6.getCanonicalPath(), false); - } - catch (Exception e) - { - showToolbarNotification (getString(R.string.error_installing_binares),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); + } + catch (Exception e) + { + showToolbarNotification (getString(R.string.error_installing_binares),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); - return false; - } + return false; + } } updateConfiguration("EntryNodes", entranceNodes, false); updateConfiguration("ExitNodes", exitNodes, false); - updateConfiguration("ExcludeNodes", excludeNodes, false); - updateConfiguration("StrictNodes", enableStrictNodes ? "1" : "0", false); + updateConfiguration("ExcludeNodes", excludeNodes, false); + updateConfiguration("StrictNodes", enableStrictNodes ? "1" : "0", false); - if (useBridges) - { - - debug ("Using bridges"); - String bridgeCfgKey = "Bridge"; + if (useBridges) + { + + debug ("Using bridges"); + String bridgeCfgKey = "Bridge"; - String bridgeList = prefs.getString(TorConstants.PREF_BRIDGES_LIST,null); + String bridgeList = prefs.getString(TorConstants.PREF_BRIDGES_LIST,null); - if (bridgeList == null || bridgeList.length() == 0) - { - String msgBridge = getString(R.string.bridge_requires_ip) + - getString(R.string.send_email_for_bridges); - showToolbarNotification(msgBridge, ERROR_NOTIFY_ID, R.drawable.ic_stat_tor); - debug(msgBridge); - - return false; - } + if (bridgeList == null || bridgeList.length() == 0) + { + String msgBridge = getString(R.string.bridge_requires_ip) + + getString(R.string.send_email_for_bridges); + showToolbarNotification(msgBridge, ERROR_NOTIFY_ID, R.drawable.ic_stat_tor); + debug(msgBridge); + + return false; + } - - String bridgeDelim = "\n"; - - if (bridgeList.indexOf(",") != -1) - { - bridgeDelim = ","; - } - - showToolbarNotification(getString(R.string.notification_using_bridges) + ": " + bridgeList, TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); + + String bridgeDelim = "\n"; + + if (bridgeList.indexOf(",") != -1) + { + bridgeDelim = ","; + } + + showToolbarNotification(getString(R.string.notification_using_bridges) + ": " + bridgeList, TRANSPROXY_NOTIFY_ID, R.drawable.ic_stat_tor); - StringTokenizer st = new StringTokenizer(bridgeList,bridgeDelim); - while (st.hasMoreTokens()) - { - String bridgeConfigLine = st.nextToken().trim(); - debug("Adding bridge: " + bridgeConfigLine); - updateConfiguration(bridgeCfgKey, bridgeConfigLine, false); + StringTokenizer st = new StringTokenizer(bridgeList,bridgeDelim); + while (st.hasMoreTokens()) + { + String bridgeConfigLine = st.nextToken().trim(); + debug("Adding bridge: " + bridgeConfigLine); + updateConfiguration(bridgeCfgKey, bridgeConfigLine, false); - } + } - //check if any PT bridges are needed - boolean obfsBridges = bridgeList.contains("obfs2")||bridgeList.contains("obfs3")||bridgeList.contains("scramblesuit"); + //check if any PT bridges are needed + boolean obfsBridges = bridgeList.contains("obfs2")||bridgeList.contains("obfs3")||bridgeList.contains("scramblesuit"); - if (obfsBridges) - { - String bridgeConfig = "obfs2,obfs3,scramblesuit exec " + fileObfsclient.getCanonicalPath(); - - debug ("Using OBFUSCATED bridges: " + bridgeConfig); - - updateConfiguration("ClientTransportPlugin",bridgeConfig, false); - } - else - { - debug ("Using standard bridges"); - } - + if (obfsBridges) + { + String bridgeConfig = "obfs2,obfs3,scramblesuit exec " + fileObfsclient.getCanonicalPath(); + + debug ("Using OBFUSCATED bridges: " + bridgeConfig); + + updateConfiguration("ClientTransportPlugin",bridgeConfig, false); + } + else + { + debug ("Using standard bridges"); + } + - updateConfiguration("UpdateBridgesFromAuthority", "0", false); - + updateConfiguration("UpdateBridgesFromAuthority", "0", false); + - updateConfiguration("UseBridges", "1", false); - - - } - else - { - updateConfiguration("UseBridges", "0", false); + updateConfiguration("UseBridges", "1", false); + + + } + else + { + updateConfiguration("UseBridges", "0", false); - } + } try { @@ -2285,7 +2231,7 @@ public class TorService extends Service implements TorServiceConstants, TorConst } catch (Exception e) { - showToolbarNotification (getString(R.string.your_reachableaddresses_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); + showToolbarNotification (getString(R.string.your_reachableaddresses_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); return false; } @@ -2301,20 +2247,20 @@ public class TorService extends Service implements TorServiceConstants, TorConst updateConfiguration("ServerDNSResolvConfFile", dnsFile, false); updateConfiguration("ORPort", ORPort + "", false); - updateConfiguration("Nickname", nickname, false); - updateConfiguration("ExitPolicy", "reject *:*", false); + updateConfiguration("Nickname", nickname, false); + updateConfiguration("ExitPolicy", "reject *:*", false); } else { - updateConfiguration("ORPort", "", false); - updateConfiguration("Nickname", "", false); - updateConfiguration("ExitPolicy", "", false); + updateConfiguration("ORPort", "", false); + updateConfiguration("Nickname", "", false); + updateConfiguration("ExitPolicy", "", false); } } catch (Exception e) { - showToolbarNotification (getString(R.string.your_relay_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); + showToolbarNotification (getString(R.string.your_relay_settings_caused_an_exception_),ERROR_NOTIFY_ID,R.drawable.ic_stat_notifyerr); return false; @@ -2322,189 +2268,188 @@ public class TorService extends Service implements TorServiceConstants, TorConst if (enableHiddenServices) { - logNotice("hidden services are enabled"); - - //updateConfiguration("RendPostPeriod", "600 seconds", false); //possible feature to investigate - - String hsPorts = prefs.getString("pref_hs_ports",""); - - StringTokenizer st = new StringTokenizer (hsPorts,","); - String hsPortConfig = null; - int hsPort = -1; - - while (st.hasMoreTokens()) - { - try - { - hsPortConfig = st.nextToken().trim(); - - if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed - { - hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig; - } - - hsPort = Integer.parseInt(hsPortConfig.split(" ")[0]); + logNotice("hidden services are enabled"); + + //updateConfiguration("RendPostPeriod", "600 seconds", false); //possible feature to investigate + + String hsPorts = prefs.getString("pref_hs_ports",""); + + StringTokenizer st = new StringTokenizer (hsPorts,","); + String hsPortConfig = null; + int hsPort = -1; + + while (st.hasMoreTokens()) + { + try + { + hsPortConfig = st.nextToken().trim(); + + if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed + { + hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig; + } + + hsPort = Integer.parseInt(hsPortConfig.split(" ")[0]); - String hsDirPath = new File(appCacheHome,"hs" + hsPort).getCanonicalPath(); - - debug("Adding hidden service on port: " + hsPortConfig); - - - updateConfiguration("HiddenServiceDir",hsDirPath, false); - updateConfiguration("HiddenServicePort",hsPortConfig, false); - + String hsDirPath = new File(appCacheHome,"hs" + hsPort).getCanonicalPath(); + + debug("Adding hidden service on port: " + hsPortConfig); + + + updateConfiguration("HiddenServiceDir",hsDirPath, false); + updateConfiguration("HiddenServicePort",hsPortConfig, false); + - } catch (NumberFormatException e) { - Log.e(this.TAG,"error parsing hsport",e); - } catch (Exception e) { - Log.e(this.TAG,"error starting share server",e); - } - } - - + } catch (NumberFormatException e) { + Log.e(this.TAG,"error parsing hsport",e); + } catch (Exception e) { + Log.e(this.TAG,"error starting share server",e); + } + } + + } else { - updateConfiguration("HiddenServiceDir","", false); - + updateConfiguration("HiddenServiceDir","", false); + } saveConfiguration(); - + return true; } /* private void enableSocks (String socks, boolean safeSocks) throws RemoteException - { - updateConfiguration("SOCKSPort", socks, false); - updateConfiguration("SafeSocks", safeSocks ? "1" : "0", false); - updateConfiguration("TestSocks", "1", false); - updateConfiguration("WarnUnsafeSocks", "1", false); - saveConfiguration(); + { + updateConfiguration("SOCKSPort", socks, false); + updateConfiguration("SafeSocks", safeSocks ? "1" : "0", false); + updateConfiguration("TestSocks", "1", false); + updateConfiguration("WarnUnsafeSocks", "1", false); + saveConfiguration(); } private void enableTransProxyAndDNSPorts (String transPort, String dnsPort) throws RemoteException { - logMessage ("Transparent Proxying: enabling port..."); - - updateConfiguration("TransPort",transPort,false); - updateConfiguration("DNSPort",dnsPort,false); - updateConfiguration("VirtualAddrNetwork","10.192.0.0/10",false); - updateConfiguration("AutomapHostsOnResolve","1",false); - saveConfiguration(); + logMessage ("Transparent Proxying: enabling port..."); + + updateConfiguration("TransPort",transPort,false); + updateConfiguration("DNSPort",dnsPort,false); + updateConfiguration("VirtualAddrNetwork","10.192.0.0/10",false); + updateConfiguration("AutomapHostsOnResolve","1",false); + saveConfiguration(); }*/ private void blockPlaintextPorts (String portList) throws RemoteException { - - updateConfiguration("RejectPlaintextPorts",portList,false); + updateConfiguration("RejectPlaintextPorts",portList,false); } //using Google DNS for now as the public DNS server private String writeDNSFile () throws IOException { - File file = new File(appBinHome,"resolv.conf"); - - PrintWriter bw = new PrintWriter(new FileWriter(file)); - bw.println("nameserver 8.8.8.8"); - bw.println("nameserver 8.8.4.4"); - bw.close(); + File file = new File(appBinHome,"resolv.conf"); + + PrintWriter bw = new PrintWriter(new FileWriter(file)); + bw.println("nameserver 8.8.8.8"); + bw.println("nameserver 8.8.4.4"); + bw.close(); - return file.getCanonicalPath(); + return file.getCanonicalPath(); } - @SuppressLint("NewApi") - @Override - public void onTrimMemory(int level) { - super.onTrimMemory(level); - - switch (level) - { - - case TRIM_MEMORY_BACKGROUND: - debug("trim memory requested: app in the background"); - return; - - /** - public static final int TRIM_MEMORY_BACKGROUND - Added in API level 14 - Level for onTrimMemory(int): the process has gone on to the LRU list. This is a good opportunity to clean up resources that can efficiently and quickly be re-built if the user returns to the app. - Constant Value: 40 (0x00000028) - */ - - case TRIM_MEMORY_COMPLETE: + @SuppressLint("NewApi") + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + + switch (level) + { + + case TRIM_MEMORY_BACKGROUND: + debug("trim memory requested: app in the background"); + return; + + /** + public static final int TRIM_MEMORY_BACKGROUND + Added in API level 14 + Level for onTrimMemory(int): the process has gone on to the LRU list. This is a good opportunity to clean up resources that can efficiently and quickly be re-built if the user returns to the app. + Constant Value: 40 (0x00000028) + */ + + case TRIM_MEMORY_COMPLETE: - debug("trim memory requested: cleanup all memory"); - return; - /** - public static final int TRIM_MEMORY_COMPLETE - Added in API level 14 - Level for onTrimMemory(int): the process is nearing the end of the background LRU list, and if more memory isn't found soon it will be killed. - Constant Value: 80 (0x00000050) - */ - case TRIM_MEMORY_MODERATE: + debug("trim memory requested: cleanup all memory"); + return; + /** + public static final int TRIM_MEMORY_COMPLETE + Added in API level 14 + Level for onTrimMemory(int): the process is nearing the end of the background LRU list, and if more memory isn't found soon it will be killed. + Constant Value: 80 (0x00000050) + */ + case TRIM_MEMORY_MODERATE: - debug("trim memory requested: clean up some memory"); - return; - - /** - public static final int TRIM_MEMORY_MODERATE - Added in API level 14 - Level for onTrimMemory(int): the process is around the middle of the background LRU list; freeing memory can help the system keep other processes running later in the list for better overall performance. - Constant Value: 60 (0x0000003c) - */ - - case TRIM_MEMORY_RUNNING_CRITICAL: + debug("trim memory requested: clean up some memory"); + return; + + /** + public static final int TRIM_MEMORY_MODERATE + Added in API level 14 + Level for onTrimMemory(int): the process is around the middle of the background LRU list; freeing memory can help the system keep other processes running later in the list for better overall performance. + Constant Value: 60 (0x0000003c) + */ + + case TRIM_MEMORY_RUNNING_CRITICAL: - debug("trim memory requested: memory on device is very low and critical"); - return; - /** - public static final int TRIM_MEMORY_RUNNING_CRITICAL - Added in API level 16 - Level for onTrimMemory(int): the process is not an expendable background process, but the device is running extremely low on memory and is about to not be able to keep any background processes running. Your running process should free up as many non-critical resources as it can to allow that memory to be used elsewhere. The next thing that will happen after this is onLowMemory() called to report that nothing at all can be kept in the background, a situation that can start to notably impact the user. - Constant Value: 15 (0x0000000f) - */ - - case TRIM_MEMORY_RUNNING_LOW: + debug("trim memory requested: memory on device is very low and critical"); + return; + /** + public static final int TRIM_MEMORY_RUNNING_CRITICAL + Added in API level 16 + Level for onTrimMemory(int): the process is not an expendable background process, but the device is running extremely low on memory and is about to not be able to keep any background processes running. Your running process should free up as many non-critical resources as it can to allow that memory to be used elsewhere. The next thing that will happen after this is onLowMemory() called to report that nothing at all can be kept in the background, a situation that can start to notably impact the user. + Constant Value: 15 (0x0000000f) + */ + + case TRIM_MEMORY_RUNNING_LOW: - debug("trim memory requested: memory on device is running low"); - return; - /** - public static final int TRIM_MEMORY_RUNNING_LOW - Added in API level 16 - Level for onTrimMemory(int): the process is not an expendable background process, but the device is running low on memory. Your running process should free up unneeded resources to allow that memory to be used elsewhere. - Constant Value: 10 (0x0000000a) - */ - case TRIM_MEMORY_RUNNING_MODERATE: + debug("trim memory requested: memory on device is running low"); + return; + /** + public static final int TRIM_MEMORY_RUNNING_LOW + Added in API level 16 + Level for onTrimMemory(int): the process is not an expendable background process, but the device is running low on memory. Your running process should free up unneeded resources to allow that memory to be used elsewhere. + Constant Value: 10 (0x0000000a) + */ + case TRIM_MEMORY_RUNNING_MODERATE: - debug("trim memory requested: memory on device is moderate"); - return; - /** - public static final int TRIM_MEMORY_RUNNING_MODERATE - Added in API level 16 - Level for onTrimMemory(int): the process is not an expendable background process, but the device is running moderately low on memory. Your running process may want to release some unneeded resources for use elsewhere. - Constant Value: 5 (0x00000005) - */ - case TRIM_MEMORY_UI_HIDDEN: + debug("trim memory requested: memory on device is moderate"); + return; + /** + public static final int TRIM_MEMORY_RUNNING_MODERATE + Added in API level 16 + Level for onTrimMemory(int): the process is not an expendable background process, but the device is running moderately low on memory. Your running process may want to release some unneeded resources for use elsewhere. + Constant Value: 5 (0x00000005) + */ + case TRIM_MEMORY_UI_HIDDEN: - debug("trim memory requested: app is not showing UI anymore"); - return; - - /** - public static final int TRIM_MEMORY_UI_HIDDEN - Level for onTrimMemory(int): the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed. - Constant Value: 20 (0x00000014) - */ - } - - } + debug("trim memory requested: app is not showing UI anymore"); + return; + + /** + public static final int TRIM_MEMORY_UI_HIDDEN + Level for onTrimMemory(int): the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed. + Constant Value: 20 (0x00000014) + */ + } + + } - @Override - public IBinder onBind(Intent arg0) { - // TODO Auto-generated method stub - return null; - } + @Override + public IBinder onBind(Intent arg0) { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/org/torproject/android/settings/AppManager.java b/src/org/torproject/android/settings/AppManager.java index 50b0b25c..6214307e 100644 --- a/src/org/torproject/android/settings/AppManager.java +++ b/src/org/torproject/android/settings/AppManager.java @@ -11,10 +11,7 @@ import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; - - - -import org.torproject.android.R; +import org.sandroproxy.ony.R; import org.torproject.android.TorConstants; import org.torproject.android.service.TorService; import org.torproject.android.service.TorServiceUtils; diff --git a/src/org/torproject/android/settings/SettingsPreferences.java b/src/org/torproject/android/settings/SettingsPreferences.java index 9a8891dd..0fd9a893 100644 --- a/src/org/torproject/android/settings/SettingsPreferences.java +++ b/src/org/torproject/android/settings/SettingsPreferences.java @@ -5,8 +5,8 @@ package org.torproject.android.settings; import java.util.Locale; +import org.sandroproxy.ony.R; import org.sufficientlysecure.rootcommands.RootCommands; -import org.torproject.android.R; import org.torproject.android.service.TorServiceUtils; import android.content.Context; diff --git a/src/org/torproject/android/vpn/OrbotVpnService.java b/src/org/torproject/android/vpn/OrbotVpnService.java index 9a58b663..66d22b8d 100644 --- a/src/org/torproject/android/vpn/OrbotVpnService.java +++ b/src/org/torproject/android/vpn/OrbotVpnService.java @@ -20,23 +20,36 @@ import java.io.IOException; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.Locale; +import java.util.Set; +import org.sandroproxy.ony.R; +import org.torproject.android.Orbot; +import org.torproject.android.service.TorService; import org.torproject.android.service.TorServiceConstants; +import com.runjva.sourceforge.jsocks.protocol.ProxyServer; +import com.runjva.sourceforge.jsocks.server.ServerAuthenticatorNone; + +import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.net.VpnService; import android.os.Build; import android.os.Handler; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.support.v4.app.NotificationCompat; import android.util.Log; +import android.widget.RemoteViews; import android.widget.Toast; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class OrbotVpnService extends VpnService implements Handler.Callback { - private static final String TAG = "OrbotVpnService"; + private static final String TAG = "DrobotVpnService"; private PendingIntent mConfigureIntent; @@ -48,14 +61,28 @@ public class OrbotVpnService extends VpnService implements Handler.Callback { private ParcelFileDescriptor mInterface; private int mSocksProxyPort = 9999; - // private ProxyServer mProxyServer; + private ProxyServer mProxyServer; private final static int VPN_MTU = 1500; + private static final int NOTIFY_ID = 10; + private static final int TRANSPROXY_NOTIFY_ID = 20; + private static final int ERROR_NOTIFY_ID = 30; + private static final int HS_NOTIFY_ID = 40; + + private boolean prefPersistNotifications = true; + + private NotificationManager mNotificationManager = null; + private android.support.v4.app.NotificationCompat.Builder mNotifyBuilder; + private Notification mNotification; + private boolean mShowExpandedNotifications = false; + private boolean mNotificationShowing = false; + + @Override public int onStartCommand(Intent intent, int flags, int startId) { - // The handler is only used to show messages. + // The handler is only used to show messages. if (mHandler == null) { mHandler = new Handler(this); } @@ -63,30 +90,46 @@ public class OrbotVpnService extends VpnService implements Handler.Callback { // Stop the previous session by interrupting the thread. if (mThreadVPN == null || (!mThreadVPN.isAlive())) { - startSocksBypass (); - setupTun2Socks(); + startSocksBypass (); + setupTun2Socks(); } return START_STICKY; } - private void startSocksBypass () - { + private void startSocksBypass(){ + mThreadProxy = new Thread () + { + public void run () + { + + try { + mProxyServer = new ProxyServer(new ServerAuthenticatorNone(null, null)); + ProxyServer.setVpnService(OrbotVpnService.this); + mProxyServer.start(mSocksProxyPort, 5, InetAddress.getLocalHost()); + } catch (Exception e) { + Log.d(TAG,"proxy server error: " + e.getLocalizedMessage(),e); + } + } + }; + + mThreadProxy.start(); } @Override public void onDestroy() { - - - - if (mInterface != null) - try { - mInterface.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + if (mProxyServer != null){ + mProxyServer.stop(); + } + if (mInterface != null){ + try { + mInterface.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } } @Override @@ -100,152 +143,158 @@ public class OrbotVpnService extends VpnService implements Handler.Callback { private void setupTun2Socks() { - mThreadVPN = new Thread () - { - - public void run () - { - if (mInterface == null) - { - // Set the locale to English (or probably any other language that^M - // uses Hindu-Arabic (aka Latin) numerals).^M - // We have found that VpnService.Builder does something locale-dependent^M - // internally that causes errors when the locale uses its own numerals^M - // (i.e., Farsi and Arabic).^M - Locale.setDefault(new Locale("en")); - - Builder builder = new Builder(); - - builder.setMtu(VPN_MTU); - builder.addAddress("10.0.0.1",8); - builder.setSession("OrbotVPN"); - builder.addRoute("0.0.0.0",0); - builder.addRoute("10.0.0.0",8); - //builder.addRoute("192.0.0.0",8); - //builder.addRoute("192.168.43.0",8); - builder.addDnsServer("8.8.8.8"); - - // Create a new interface using the builder and save the parameters. - mInterface = builder.setSession(mSessionName) - .setConfigureIntent(mConfigureIntent) - .establish(); - - try - { - Tun2Socks.Start(mInterface, VPN_MTU, "10.0.0.2", "255.255.255.0", "localhost:" + TorServiceConstants.PORT_SOCKS_DEFAULT, "50.116.51.157:7300", true); - } - catch (Exception e) - { - Log.d(TAG,"tun2Socks has stopped",e); - } - } - } - }; - - mThreadVPN.start(); + mThreadVPN = new Thread () + { + + public void run () + { + if (mInterface == null) + { + // Set the locale to English (or probably any other language that^M + // uses Hindu-Arabic (aka Latin) numerals).^M + // We have found that VpnService.Builder does something locale-dependent^M + // internally that causes errors when the locale uses its own numerals^M + // (i.e., Farsi and Arabic).^M + Locale.setDefault(new Locale("en")); + + Builder builder = new Builder(); + + builder.setMtu(VPN_MTU); + builder.addAddress("10.0.0.1",28); + builder.setSession("DrobotVPN"); + builder.addRoute("0.0.0.0",0); + //builder.addRoute("192.0.0.0",8); + //builder.addRoute("192.168.43.0",8); + + // Create a new interface using the builder and save the parameters. + mInterface = builder.setSession(mSessionName) + .setConfigureIntent(mConfigureIntent) + .establish(); + + try + { + Tun2Socks.Start(mInterface, + VPN_MTU, + "10.0.0.2", + "255.255.255.0", + "127.0.0.1:" + TorServiceConstants.PORT_SOCKS_DEFAULT, + "10.0.0.1:" + String.valueOf(TorServiceConstants.TOR_DNS_PORT_DEFAULT), + true); + + } + catch (Exception e) + { + Log.d(TAG,"tun2Socks has stopped",e); + } + } + } + }; + mThreadVPN.start(); + showToolbarNotification(getString(R.string.status_activated), NOTIFY_ID, R.drawable.ic_stat_tor); } - @Override - public void onRevoke() { - - new Thread () - { - public void run() - { - try - { - mInterface.close(); - Tun2Socks.Stop(); - } - catch (Exception e) - { - Log.d(TAG,"error stopping tun2socks",e); - } - } - }.start(); - - super.onRevoke(); - - } + @Override + public void onRevoke() { + + new Thread () + { + public void run() + { + try + { + mInterface.close(); + Tun2Socks.Stop(); + } + catch (Exception e) + { + Log.d(TAG,"error stopping tun2socks",e); + } + } + }.start(); + clearNotifications(); + super.onRevoke(); + } - /* - private void debugPacket(ByteBuffer packet) + private void clearNotifications() { + if (mNotificationManager != null) + mNotificationManager.cancelAll(); + mNotificationShowing = false; + + } + + + @SuppressLint("NewApi") + private void showToolbarNotification (String notifyMsg, int notifyType, int icon) + { + + //Reusable code. + Intent intent = new Intent(OrbotVpnService.this, Orbot.class); + PendingIntent pendIntent = PendingIntent.getActivity(OrbotVpnService.this, 0, intent, 0); + + if (mNotifyBuilder == null) + { + + mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + if (mNotifyBuilder == null) + { + mNotifyBuilder = new NotificationCompat.Builder(this) + .setContentTitle(getString(R.string.app_name)) + .setSmallIcon(R.drawable.ic_stat_tor); + mNotifyBuilder.setContentIntent(pendIntent); + } + } - int buffer = packet.get(); - int version; - int headerlength; - version = buffer >> 4; - headerlength = buffer & 0x0F; - headerlength *= 4; - Log.d(TAG, "IP Version:"+version); - Log.d(TAG, "Header Length:"+headerlength); - - String status = ""; - status += "Header Length:"+headerlength; - - buffer = packet.get(); //DSCP + EN - buffer = packet.getChar(); //Total Length - - Log.d(TAG, "Total Length:"+buffer); - - buffer = packet.getChar(); //Identification - Log.d(TAG, "Identification:"+buffer); - - buffer = packet.getChar(); //Flags + Fragment Offset - buffer = packet.get(); //Time to Live - buffer = packet.get(); //Protocol - - Log.d(TAG, "Protocol:"+buffer); - - status += " Protocol:"+buffer; - - buffer = packet.getChar(); //Header checksum - - String sourceIP = ""; - buffer = packet.get(); //Source IP 1st Octet - sourceIP += buffer; - sourceIP += "."; - - buffer = packet.get(); //Source IP 2nd Octet - sourceIP += buffer; - sourceIP += "."; - - buffer = packet.get(); //Source IP 3rd Octet - sourceIP += buffer; - sourceIP += "."; - - buffer = packet.get(); //Source IP 4th Octet - sourceIP += buffer; - - Log.d(TAG, "Source IP:"+sourceIP); - - status += " Source IP:"+sourceIP; - - String destIP = ""; - buffer = packet.get(); //Destination IP 1st Octet - destIP += buffer; - destIP += "."; - - buffer = packet.get(); //Destination IP 2nd Octet - destIP += buffer; - destIP += "."; - - buffer = packet.get(); //Destination IP 3rd Octet - destIP += buffer; - destIP += "."; - - buffer = packet.get(); //Destination IP 4th Octet - destIP += buffer; - - Log.d(TAG, "Destination IP:"+destIP); - - status += " Destination IP:"+destIP; - - //Log.d(TAG, "version:"+packet.getInt()); - //Log.d(TAG, "version:"+packet.getInt()); - //Log.d(TAG, "version:"+packet.getInt()); - - }*/ - + mNotifyBuilder.setContentText(notifyMsg); + mNotifyBuilder.setSmallIcon(icon); + + if (notifyType != NOTIFY_ID) + { + mNotifyBuilder.setTicker(notifyMsg); + // mNotifyBuilder.setLights(Color.GREEN, 1000, 1000); + } + else + { + mNotifyBuilder.setTicker(null); + } + + mNotifyBuilder.setOngoing(prefPersistNotifications); + + mNotification = mNotifyBuilder.build(); + + if (Build.VERSION.SDK_INT >= 16 && mShowExpandedNotifications) { + + + // Create remote view that needs to be set as bigContentView for the notification. + RemoteViews expandedView = new RemoteViews(this.getPackageName(), + R.layout.layout_notification_expanded); + + StringBuffer sbInfo = new StringBuffer(); + + + if (notifyType == NOTIFY_ID) + expandedView.setTextViewText(R.id.text, notifyMsg); + else + { + expandedView.setTextViewText(R.id.info, notifyMsg); + + } + expandedView.setTextViewText(R.id.title, getString(R.string.app_name)); + + expandedView.setImageViewResource(R.id.icon, icon); + mNotification.bigContentView = expandedView; + } + + if (prefPersistNotifications && (!mNotificationShowing)) + { + startForeground(NOTIFY_ID, mNotification); + } + else + { + mNotificationManager.notify(NOTIFY_ID, mNotification); + } + + mNotificationShowing = true; + } } diff --git a/src/org/torproject/android/vpn/Tun2Socks.java b/src/org/torproject/android/vpn/Tun2Socks.java index fa60dbd9..a7c638e9 100644 --- a/src/org/torproject/android/vpn/Tun2Socks.java +++ b/src/org/torproject/android/vpn/Tun2Socks.java @@ -25,6 +25,7 @@ import java.net.Socket; import android.annotation.TargetApi; import android.os.Build; import android.os.ParcelFileDescriptor; +import android.util.Log; @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) public class Tun2Socks @@ -35,6 +36,8 @@ public class Tun2Socks boolean doVpnProtect(DatagramSocket socket); }; + private static final String TAG = Tun2Socks.class.getSimpleName(); + private static final boolean LOGD = true; private static Thread mThread; private static ParcelFileDescriptor mVpnInterfaceFileDescriptor; @@ -61,11 +64,11 @@ public class Tun2Socks boolean udpgwTransparentDNS) { - if (!mLibLoaded) - { - System.loadLibrary("tun2socks"); - mLibLoaded = true; - } + if (!mLibLoaded) + { + System.loadLibrary("tun2socks"); + mLibLoaded = true; + } mVpnInterfaceFileDescriptor = vpnInterfaceFileDescriptor; mVpnInterfaceMTU = vpnInterfaceMTU; @@ -76,16 +79,14 @@ public class Tun2Socks mUdpgwTransparentDNS = udpgwTransparentDNS; if (mVpnInterfaceFileDescriptor != null) - runTun2Socks( - mVpnInterfaceFileDescriptor.detachFd(), - mVpnInterfaceMTU, - mVpnIpAddress, - mVpnNetMask, - mSocksServerAddress, - mUdpgwServerAddress, - mUdpgwTransparentDNS ? 1 : 0); - - + runTun2Socks( + mVpnInterfaceFileDescriptor.detachFd(), + mVpnInterfaceMTU, + mVpnIpAddress, + mVpnNetMask, + mSocksServerAddress, + mUdpgwServerAddress, + mUdpgwTransparentDNS ? 1 : 0); } public static void Stop() @@ -94,7 +95,23 @@ public class Tun2Socks terminateTun2Socks(); } - + + public static void logTun2Socks( + String level, + String channel, + String msg) + { + String logMsg = level + "(" + channel + "): " + msg; + if (0 == level.compareTo("ERROR")) + { + Log.e(TAG, logMsg); + } + else + { + if (LOGD) Log.d(TAG, logMsg); + } + } + private native static int runTun2Socks( int vpnInterfaceFileDescriptor, int vpnInterfaceMTU, diff --git a/src/org/torproject/android/wizard/ChooseLocaleWizardActivity.java b/src/org/torproject/android/wizard/ChooseLocaleWizardActivity.java index 1a620c19..8325bb58 100644 --- a/src/org/torproject/android/wizard/ChooseLocaleWizardActivity.java +++ b/src/org/torproject/android/wizard/ChooseLocaleWizardActivity.java @@ -2,7 +2,7 @@ package org.torproject.android.wizard; import java.util.Locale; -import org.torproject.android.R; +import org.sandroproxy.ony.R; import org.torproject.android.TorConstants; import org.torproject.android.service.TorServiceUtils; @@ -24,66 +24,66 @@ import android.widget.Toast; public class ChooseLocaleWizardActivity extends Activity implements TorConstants { - private int flag = 0; - private ListView listLocales; - - protected void onCreate(Bundle savedInstanceState) - { + private int flag = 0; + private ListView listLocales; + + protected void onCreate(Bundle savedInstanceState) + { super.onCreate(savedInstanceState); - } - - @Override - protected void onStart() { - - super.onStart(); - setContentView(R.layout.layout_wizard_locale); - - - listLocales = (ListView)findViewById(R.id.wizard_locale_list); - Button next = ((Button)findViewById(R.id.btnWizard2)); - // next.setEnabled(false); - - String[] strLangs = getResources().getStringArray(R.array.languages); - strLangs[0] = Locale.getDefault().getDisplayName(); - ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, strLangs); + } + + @Override + protected void onStart() { + + super.onStart(); + setContentView(R.layout.layout_wizard_locale); + + + listLocales = (ListView)findViewById(R.id.wizard_locale_list); + Button next = ((Button)findViewById(R.id.btnWizard2)); + // next.setEnabled(false); + + String[] strLangs = getResources().getStringArray(R.array.languages); + strLangs[0] = Locale.getDefault().getDisplayName(); + ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, strLangs); listLocales.setAdapter(adapter); - - listLocales.setSelection(0); - - - listLocales.setOnItemClickListener(new OnItemClickListener() { - + + listLocales.setSelection(0); + + + listLocales.setOnItemClickListener(new OnItemClickListener() { + - @Override - public void onItemClick(AdapterView arg0, View arg1, - int arg2, long arg3) { - - setLocalePref(arg2); - finish(); - startActivity(new Intent(ChooseLocaleWizardActivity.this, LotsaText.class)); - - } - }); - - next.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - setLocalePref(0); - finish(); - startActivity(new Intent(ChooseLocaleWizardActivity.this, LotsaText.class)); + @Override + public void onItemClick(AdapterView arg0, View arg1, + int arg2, long arg3) { + + setLocalePref(arg2); + finish(); + startActivity(new Intent(ChooseLocaleWizardActivity.this, LotsaText.class)); + + } + }); + + next.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + setLocalePref(0); + finish(); + startActivity(new Intent(ChooseLocaleWizardActivity.this, LotsaText.class)); - } - }); - + } + }); + - - } - - private void setLocalePref(int selId) - { + + } + + private void setLocalePref(int selId) + { - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); Configuration config = getResources().getConfiguration(); @@ -91,18 +91,18 @@ public class ChooseLocaleWizardActivity extends Activity implements TorConstants String lang = localeVals[selId]; - Editor pEdit = prefs.edit(); - pEdit.putString(PREF_DEFAULT_LOCALE, lang); - pEdit.commit(); - Locale locale = null; - + Editor pEdit = prefs.edit(); + pEdit.putString(PREF_DEFAULT_LOCALE, lang); + pEdit.commit(); + Locale locale = null; + if (lang.equals("xx")) { - locale = Locale.getDefault(); + locale = Locale.getDefault(); } else - locale = new Locale(lang); + locale = new Locale(lang); Locale.setDefault(locale); config.locale = locale; @@ -110,27 +110,27 @@ public class ChooseLocaleWizardActivity extends Activity implements TorConstants - - } + + } - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - } + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + } - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - } - - //Code to override the back button! - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK){ - Toast.makeText(getApplicationContext(), R.string.wizard_exit_at_first_screen_toast, Toast.LENGTH_SHORT).show(); - return true; - } - return false; - } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + } + + //Code to override the back button! + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if(keyCode == KeyEvent.KEYCODE_BACK){ + Toast.makeText(getApplicationContext(), R.string.wizard_exit_at_first_screen_toast, Toast.LENGTH_SHORT).show(); + return true; + } + return false; + } } \ No newline at end of file diff --git a/src/org/torproject/android/wizard/ConfigureTransProxy.java b/src/org/torproject/android/wizard/ConfigureTransProxy.java index ca7f3aca..d8e5a1ae 100644 --- a/src/org/torproject/android/wizard/ConfigureTransProxy.java +++ b/src/org/torproject/android/wizard/ConfigureTransProxy.java @@ -1,12 +1,8 @@ package org.torproject.android.wizard; +import org.sandroproxy.ony.R; import org.torproject.android.Orbot; -import org.torproject.android.R; import org.torproject.android.TorConstants; -import org.torproject.android.R.drawable; -import org.torproject.android.R.id; -import org.torproject.android.R.layout; -import org.torproject.android.R.string; import org.torproject.android.service.TorServiceUtils; import org.torproject.android.settings.AppManager; diff --git a/src/org/torproject/android/wizard/LotsaText.java b/src/org/torproject/android/wizard/LotsaText.java index 536edabf..669aa02a 100644 --- a/src/org/torproject/android/wizard/LotsaText.java +++ b/src/org/torproject/android/wizard/LotsaText.java @@ -1,6 +1,6 @@ package org.torproject.android.wizard; -import org.torproject.android.R; +import org.sandroproxy.ony.R; import org.torproject.android.TorConstants; import org.torproject.android.service.TorServiceUtils; diff --git a/src/org/torproject/android/wizard/Permissions.java b/src/org/torproject/android/wizard/Permissions.java index 229f5bdc..500d785f 100644 --- a/src/org/torproject/android/wizard/Permissions.java +++ b/src/org/torproject/android/wizard/Permissions.java @@ -1,7 +1,7 @@ package org.torproject.android.wizard; +import org.sandroproxy.ony.R; import org.sufficientlysecure.rootcommands.RootCommands; -import org.torproject.android.R; import org.torproject.android.TorConstants; import org.torproject.android.service.TorServiceUtils; @@ -22,49 +22,49 @@ import android.widget.TextView; public class Permissions extends Activity implements TorConstants { - private Context context; - - protected void onCreate(Bundle savedInstanceState) - { + private Context context; + + protected void onCreate(Bundle savedInstanceState) + { super.onCreate(savedInstanceState); context = this; - } - - @Override - protected void onStart() { - - super.onStart(); - setContentView(R.layout.layout_wizard_permissions); - - stepFourRoot(); - - } - - @Override - protected void onResume() { - super.onResume(); - - - } - - - private void stepFourRoot(){ - - String msg1 = context.getString(R.string.wizard_permissions_root_msg1); - String msg2 = context.getString(R.string.wizard_permissions_root_msg2); - + } + + @Override + protected void onStart() { + + super.onStart(); + setContentView(R.layout.layout_wizard_permissions); + + stepFourRoot(); + + } + + @Override + protected void onResume() { + super.onResume(); + + + } + + + private void stepFourRoot(){ + + String msg1 = context.getString(R.string.wizard_permissions_root_msg1); + String msg2 = context.getString(R.string.wizard_permissions_root_msg2); + TextView txtBody1 = ((TextView)findViewById(R.id.WizardTextBody1)); - txtBody1.setText(msg1); - + txtBody1.setText(msg1); + TextView txtBody2 = ((TextView)findViewById(R.id.WizardTextBody2)); - txtBody2.setText(msg2); - txtBody2.setVisibility(TextView.VISIBLE); - - Button grantPermissions = ((Button)findViewById(R.id.grantPermissions)); - grantPermissions.setVisibility(Button.VISIBLE); - + txtBody2.setText(msg2); + txtBody2.setVisibility(TextView.VISIBLE); + + Button grantPermissions = ((Button)findViewById(R.id.grantPermissions)); + grantPermissions.setVisibility(Button.VISIBLE); + Button back = ((Button)findViewById(R.id.btnWizard1)); Button next = ((Button)findViewById(R.id.btnWizard2)); next.setEnabled(false); @@ -74,133 +74,133 @@ public class Permissions extends Activity implements TorConstants { consent.setOnCheckedChangeListener(new OnCheckedChangeListener (){ - public void onCheckedChanged(CompoundButton buttonView, - boolean isChecked) { - - - //this is saying do not use root - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + public void onCheckedChanged(CompoundButton buttonView, + boolean isChecked) { + + + //this is saying do not use root + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - Editor pEdit = prefs.edit(); - - pEdit.putBoolean(PREF_TRANSPARENT, false); - pEdit.putBoolean(PREF_TRANSPARENT_ALL, false); - pEdit.putBoolean(PREF_HAS_ROOT, false); - - pEdit.commit(); - - /* - Button next = ((Button)findViewById(R.id.btnWizard2)); - if(isChecked) - next.setEnabled(true); - else - next.setEnabled(false); - */ - - stepFour(); - - } - + Editor pEdit = prefs.edit(); + + pEdit.putBoolean(PREF_TRANSPARENT, false); + pEdit.putBoolean(PREF_TRANSPARENT_ALL, false); + pEdit.putBoolean(PREF_HAS_ROOT, false); + + pEdit.commit(); + + /* + Button next = ((Button)findViewById(R.id.btnWizard2)); + if(isChecked) + next.setEnabled(true); + else + next.setEnabled(false); + */ + + stepFour(); + + } + }); grantPermissions.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - //Check and Install iptables - TorTransProxy.testOwnerModule(this) - - SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); - - boolean hasRoot = RootCommands.rootAccessGiven(); - Editor pEdit = prefs.edit(); - pEdit.putBoolean(PREF_HAS_ROOT,hasRoot); - pEdit.commit(); - - if (!hasRoot) - { + + public void onClick(View v) { + //Check and Install iptables - TorTransProxy.testOwnerModule(this) + + SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext()); + + boolean hasRoot = RootCommands.rootAccessGiven(); + Editor pEdit = prefs.edit(); + pEdit.putBoolean(PREF_HAS_ROOT,hasRoot); + pEdit.commit(); + + if (!hasRoot) + { - stepFour(); - - } - else - { - finish(); - startActivity(new Intent(Permissions.this, ConfigureTransProxy.class)); - } + stepFour(); + + } + else + { + finish(); + startActivity(new Intent(Permissions.this, ConfigureTransProxy.class)); + } - - } - }); + + } + }); + + back.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + finish(); + startActivity(new Intent(Permissions.this, LotsaText.class)); + } + }); + + + next.setOnClickListener(new View.OnClickListener() { + + + public void onClick(View v) { + finish(); + startActivity(new Intent(Permissions.this, TipsAndTricks.class)); + } + }); + + } + + private void stepFour(){ + + String title = context.getString(R.string.wizard_permissions_title); + String msg = context.getString(R.string.wizard_permissions_no_root_msg); + + setTitle(title); - back.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - startActivity(new Intent(Permissions.this, LotsaText.class)); - } - }); - - - next.setOnClickListener(new View.OnClickListener() { - - - public void onClick(View v) { - finish(); - startActivity(new Intent(Permissions.this, TipsAndTricks.class)); - } - }); - - } - - private void stepFour(){ - - String title = context.getString(R.string.wizard_permissions_title); - String msg = context.getString(R.string.wizard_permissions_no_root_msg); - - setTitle(title); - TextView txtBody = ((TextView)findViewById(R.id.WizardTextBody1)); - txtBody.setText(msg); - + txtBody.setText(msg); + Button btn1 = ((Button)findViewById(R.id.btnWizard1)); Button btn2 = ((Button)findViewById(R.id.btnWizard2)); btn2.setEnabled(true); TextView txtBody2 = ((TextView)findViewById(R.id.WizardTextBody2)); - txtBody2.setVisibility(TextView.GONE); - - Button grantPermissions = ((Button)findViewById(R.id.grantPermissions)); - grantPermissions.setVisibility(Button.GONE); + txtBody2.setVisibility(TextView.GONE); + + Button grantPermissions = ((Button)findViewById(R.id.grantPermissions)); + grantPermissions.setVisibility(Button.GONE); CheckBox consent = (CheckBox)findViewById(R.id.checkBox); consent.setVisibility(CheckBox.GONE); - btn1.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - startActivity(new Intent(Permissions.this, LotsaText.class)); - } - }); - - btn2.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - startActivity(new Intent(Permissions.this, TipsAndTricks.class)); - } - }); - } - - //Code to override the back button! - public boolean onKeyDown(int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK){ - finish(); - startActivity(new Intent(getBaseContext(), LotsaText.class)); - return true; - } - return false; - } - + btn1.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + finish(); + startActivity(new Intent(Permissions.this, LotsaText.class)); + } + }); + + btn2.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + finish(); + startActivity(new Intent(Permissions.this, TipsAndTricks.class)); + } + }); + } + + //Code to override the back button! + public boolean onKeyDown(int keyCode, KeyEvent event) { + if(keyCode == KeyEvent.KEYCODE_BACK){ + finish(); + startActivity(new Intent(getBaseContext(), LotsaText.class)); + return true; + } + return false; + } + } \ No newline at end of file diff --git a/src/org/torproject/android/wizard/TipsAndTricks.java b/src/org/torproject/android/wizard/TipsAndTricks.java index d10278ba..4034c395 100644 --- a/src/org/torproject/android/wizard/TipsAndTricks.java +++ b/src/org/torproject/android/wizard/TipsAndTricks.java @@ -1,7 +1,7 @@ package org.torproject.android.wizard; +import org.sandroproxy.ony.R; import org.torproject.android.Orbot; -import org.torproject.android.R; import org.torproject.android.TorConstants; import android.app.Activity; @@ -17,233 +17,229 @@ import android.widget.TextView; public class TipsAndTricks extends Activity implements TorConstants { - protected void onCreate(Bundle savedInstanceState) - { + protected void onCreate(Bundle savedInstanceState) + { super.onCreate(savedInstanceState); - } - - @Override - protected void onStart() { - - super.onStart(); - setContentView(R.layout.layout_wizard_tips); - - stepFive(); + } + + @Override + protected void onStart() { - } - - @Override - protected void onResume() { - super.onResume(); - - - } + super.onStart(); + setContentView(R.layout.layout_wizard_tips); + + stepFive(); + + } + + @Override + protected void onResume() { + super.onResume(); + + + } - void stepFive(){ - - - String title = getString(R.string.wizard_tips_title); - - setTitle(title); - + void stepFive(){ + + + String title = getString(R.string.wizard_tips_title); + + setTitle(title); + Button btnLink = (Button)findViewById(R.id.WizardRootButtonInstallGibberbot); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { + + public void onClick(View view) { - String url = getString(R.string.gibberbot_apk_url); - finish(); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + String url = getString(R.string.gibberbot_apk_url); + finish(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallOrweb); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.orweb_apk_url); - finish(); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.orweb_apk_url); + finish(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallDuckgo); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.duckgo_apk_url); - finish(); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.duckgo_apk_url); + finish(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallFirefox); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.proxymob_setup_url); - finish(); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.proxymob_setup_url); + finish(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallTwitter); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.twitter_setup_url); - finish(); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.twitter_setup_url); + finish(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallStoryMaker); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.story_maker_url); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.story_maker_url); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonInstallMartus); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.martus_url); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.martus_url); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); btnLink = (Button)findViewById(R.id.WizardRootButtonGooglePlay); btnLink.setOnClickListener(new OnClickListener() { - - public void onClick(View view) { - - String url = getString(R.string.wizard_tips_play_url); - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + + public void onClick(View view) { + + String url = getString(R.string.wizard_tips_play_url); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - }); + } + }); Button back = ((Button)findViewById(R.id.btnWizard1)); Button next = ((Button)findViewById(R.id.btnWizard2)); back.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - startActivityForResult(new Intent(TipsAndTricks.this, Permissions.class), 1); - } - }); - - next.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - showWizardFinal(); - } - }); + + public void onClick(View v) { + finish(); + startActivityForResult(new Intent(TipsAndTricks.this, Permissions.class), 1); + } + }); - } - - private void showWizardFinal () - { - setContentView(R.layout.scrollingtext_buttons_view); - String title = getString(R.string.wizard_final); - String msg = getString(R.string.wizard_final_msg); - - setTitle(title); + next.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + showWizardFinal(); + } + }); + + } + + private void showWizardFinal () + { + setContentView(R.layout.scrollingtext_buttons_view); + String title = getString(R.string.wizard_final); + String msg = getString(R.string.wizard_final_msg); + + setTitle(title); TextView txtBody = ((TextView)findViewById(R.id.WizardTextBody)); - txtBody.setText(msg); - + txtBody.setText(msg); + Button btn1 = ((Button)findViewById(R.id.btnWizard1)); Button btn2 = ((Button)findViewById(R.id.btnWizard2)); btn2.setText(getString(R.string.btn_finish)); - btn1.setVisibility(Button.VISIBLE); - - btn1.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - startActivity(new Intent(TipsAndTricks.this, Permissions.class)); - } - }); - - btn2.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - finish(); - } - }); - } - - //Code to override the back button! - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK){ - finish(); - startActivity(new Intent(getBaseContext(), Permissions.class)); - return true; - } - return false; - } - - /* - private void showWizardFinal () - { - String title = null; - String msg = null; - - - title = context.getString(R.string.wizard_final); - msg = context.getString(R.string.wizard_final_msg); - - DialogInterface.OnClickListener ocListener = new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - context.startActivity(new Intent(context, Orbot.class)); + btn1.setVisibility(Button.VISIBLE); + + btn1.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + finish(); + startActivity(new Intent(TipsAndTricks.this, Permissions.class)); + } + }); + + btn2.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + finish(); + } + }); + } + + //Code to override the back button! + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if(keyCode == KeyEvent.KEYCODE_BACK){ + finish(); + startActivity(new Intent(getBaseContext(), Permissions.class)); + return true; + } + return false; + } + + /* + private void showWizardFinal () + { + String title = null; + String msg = null; + + + title = context.getString(R.string.wizard_final); + msg = context.getString(R.string.wizard_final_msg); + + DialogInterface.OnClickListener ocListener = new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + context.startActivity(new Intent(context, Orbot.class)); - } - }; - - - new AlertDialog.Builder(context) - .setIcon(R.drawable.icon) + } + }; + + + new AlertDialog.Builder(context) + .setIcon(R.drawable.icon) .setTitle(title) .setPositiveButton(R.string.button_close, ocListener) .setMessage(msg) .show(); - - - - - }*/ + }*/ } \ No newline at end of file From 1472b4e35d19bce33aef84e26cdcb7ae9ee45050 Mon Sep 17 00:00:00 2001 From: SandroB Date: Sun, 25 Jan 2015 11:42:15 +0100 Subject: [PATCH 2/5] some missing files added to git repo --- external/Makefile | 2 +- jni/Application.mk | 3 + .../runjva/sourceforge/jsocks/main/SOCKS.java | 269 +++++++ .../sourceforge/jsocks/main/SocksEcho.gif | Bin 0 -> 926 bytes .../jsocks/protocol/Authentication.java | 35 + .../jsocks/protocol/AuthenticationNone.java | 22 + .../jsocks/protocol/InetRange.java | 492 +++++++++++++ .../jsocks/protocol/ProxyMessage.java | 118 +++ .../jsocks/protocol/ProxyServer.java | 669 ++++++++++++++++++ .../jsocks/protocol/Socks4Message.java | 167 +++++ .../jsocks/protocol/Socks4Proxy.java | 144 ++++ .../jsocks/protocol/Socks5DatagramSocket.java | 485 +++++++++++++ .../jsocks/protocol/Socks5Message.java | 330 +++++++++ .../jsocks/protocol/Socks5Proxy.java | 295 ++++++++ .../jsocks/protocol/SocksException.java | 111 +++ .../jsocks/protocol/SocksProxyBase.java | 543 ++++++++++++++ .../jsocks/protocol/SocksServerSocket.java | 238 +++++++ .../jsocks/protocol/SocksSocket.java | 389 ++++++++++ .../jsocks/protocol/UDPEncapsulation.java | 33 + .../jsocks/protocol/UDPRelayServer.java | 231 ++++++ .../protocol/UserPasswordAuthentication.java | 91 +++ .../sourceforge/jsocks/server/Ident.java | 176 +++++ .../jsocks/server/IdentAuthenticator.java | 182 +++++ .../jsocks/server/ServerAuthenticator.java | 126 ++++ .../server/ServerAuthenticatorBase.java | 187 +++++ .../server/ServerAuthenticatorNone.java | 16 + .../server/UserPasswordAuthenticator.java | 82 +++ .../jsocks/server/UserValidation.java | 24 + src/org/sandroproxy/ony/OrbotApp.java | 69 ++ 29 files changed, 5528 insertions(+), 1 deletion(-) create mode 100644 jni/Application.mk create mode 100644 src/com/runjva/sourceforge/jsocks/main/SOCKS.java create mode 100644 src/com/runjva/sourceforge/jsocks/main/SocksEcho.gif create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Authentication.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/AuthenticationNone.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/InetRange.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/ProxyMessage.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/ProxyServer.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Socks4Message.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Socks4Proxy.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Socks5DatagramSocket.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Socks5Message.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/Socks5Proxy.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/SocksException.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/SocksProxyBase.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/SocksServerSocket.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/SocksSocket.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/UDPEncapsulation.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/UDPRelayServer.java create mode 100644 src/com/runjva/sourceforge/jsocks/protocol/UserPasswordAuthentication.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/Ident.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/IdentAuthenticator.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/ServerAuthenticator.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorBase.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorNone.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/UserPasswordAuthenticator.java create mode 100644 src/com/runjva/sourceforge/jsocks/server/UserValidation.java create mode 100644 src/org/sandroproxy/ony/OrbotApp.java diff --git a/external/Makefile b/external/Makefile index df7de72c..8e0e0826 100644 --- a/external/Makefile +++ b/external/Makefile @@ -52,7 +52,7 @@ STRIP := $(NDK_TOOLCHAIN_BASE)/bin/$(HOST)-strip \ --strip-unneeded -R .note -R .comment # PIEFLAGS for SDK 16/Android L must be set to -fPIE -pie -PIEFLAGS? = +PIEFLAGS = -fPIE -pie CFLAGS = -DANDROID $(TARGET_CFLAGS) $(PIEFLAGS) LDFLAGS = -llog $(TARGET_LDFLAGS) $(PIEFLAGS) diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 00000000..0cfcc0f5 --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,3 @@ +APP_OPTIM := release +APP_ABI := all #armeabi armeabi-v7a mips x86 +APP_PLATFORM := android-14 diff --git a/src/com/runjva/sourceforge/jsocks/main/SOCKS.java b/src/com/runjva/sourceforge/jsocks/main/SOCKS.java new file mode 100644 index 00000000..9135f871 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/main/SOCKS.java @@ -0,0 +1,269 @@ +package com.runjva.sourceforge.jsocks.main; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.runjva.sourceforge.jsocks.protocol.InetRange; +import com.runjva.sourceforge.jsocks.protocol.ProxyServer; +import com.runjva.sourceforge.jsocks.protocol.SocksProxyBase; +import com.runjva.sourceforge.jsocks.server.IdentAuthenticator; + +public class SOCKS { + + private static final int DEFAULT_LISTENING_PORT = 1080; + final private static Logger log = LoggerFactory.getLogger(SOCKS.class); + + static public void usage() { + System.out.println("Usage: java SOCKS [inifile1 inifile2 ...]\n" + + "If none inifile is given, uses socks.properties.\n"); + } + + static public void main(String[] args) { + + String[] file_names; + int port = DEFAULT_LISTENING_PORT; + String logFile = null; + String host = null; + + final IdentAuthenticator auth = new IdentAuthenticator(); + + InetAddress localIP = null; + + if (args.length == 0) { + file_names = new String[] { "socks.properties" }; + } else { + file_names = args; + } + + inform("Loading properties"); + for (int i = 0; i < file_names.length; ++i) { + + inform("Reading file " + file_names[i]); + + final Properties pr = loadProperties(file_names[i]); + if (pr == null) { + System.err.println("Loading of properties from " + + file_names[i] + "failed."); + usage(); + return; + } + if (!addAuth(auth, pr)) { + System.err.println("Error in file " + file_names[i] + "."); + usage(); + return; + } + // First file should contain all global settings, + // like port and host and log. + if (i == 0) { + final String port_s = (String) pr.get("port"); + if (port_s != null) { + try { + port = Integer.parseInt(port_s); + } catch (final NumberFormatException nfe) { + System.err.println("Can't parse port: " + port_s); + return; + } + } + + serverInit(pr); + logFile = (String) pr.get("log"); + host = (String) pr.get("host"); + } + + // inform("Props:"+pr); + } + + if (logFile != null) { + System.err.println("log property not supported anymore."); + } + if (host != null) { + try { + localIP = InetAddress.getByName(host); + } catch (final UnknownHostException uhe) { + System.err.println("Can't resolve local ip: " + host); + return; + } + } + + inform("Using Ident Authentication scheme: " + auth); + final ProxyServer server = new ProxyServer(auth); + server.start(port, 5, localIP); + } + + static Properties loadProperties(String file_name) { + + final Properties pr = new Properties(); + + try { + final InputStream fin = new FileInputStream(file_name); + pr.load(fin); + fin.close(); + } catch (final IOException ioe) { + return null; + } + return pr; + } + + static boolean addAuth(IdentAuthenticator ident, Properties pr) { + + InetRange irange; + + final String range = (String) pr.get("range"); + if (range == null) { + return false; + } + irange = parseInetRange(range); + + final String users = (String) pr.get("users"); + + if (users == null) { + ident.add(irange, null); + return true; + } + + final Hashtable uhash = new Hashtable(); + + final StringTokenizer st = new StringTokenizer(users, ";"); + while (st.hasMoreTokens()) { + uhash.put(st.nextToken(), ""); + } + + ident.add(irange, uhash); + return true; + } + + /** + * Does server initialisation. + */ + static void serverInit(Properties props) { + int val; + val = readInt(props, "iddleTimeout"); + if (val >= 0) { + ProxyServer.setIddleTimeout(val); + inform("Setting iddle timeout to " + val + " ms."); + } + val = readInt(props, "acceptTimeout"); + if (val >= 0) { + ProxyServer.setAcceptTimeout(val); + inform("Setting accept timeout to " + val + " ms."); + } + val = readInt(props, "udpTimeout"); + if (val >= 0) { + ProxyServer.setUDPTimeout(val); + inform("Setting udp timeout to " + val + " ms."); + } + + val = readInt(props, "datagramSize"); + if (val >= 0) { + ProxyServer.setDatagramSize(val); + inform("Setting datagram size to " + val + " bytes."); + } + + proxyInit(props); + + } + + /** + * Initialises proxy, if any specified. + */ + static void proxyInit(Properties props) { + String proxy_list; + SocksProxyBase proxy = null; + StringTokenizer st; + + proxy_list = (String) props.get("proxy"); + if (proxy_list == null) { + return; + } + + st = new StringTokenizer(proxy_list, ";"); + while (st.hasMoreTokens()) { + final String proxy_entry = st.nextToken(); + + final SocksProxyBase p = SocksProxyBase.parseProxy(proxy_entry); + + if (p == null) { + exit("Can't parse proxy entry:" + proxy_entry); + } + + inform("Adding Proxy:" + p); + + if (proxy != null) { + p.setChainProxy(proxy); + } + + proxy = p; + + } + if (proxy == null) { + return; // Empty list + } + + final String direct_hosts = (String) props.get("directHosts"); + if (direct_hosts != null) { + final InetRange ir = parseInetRange(direct_hosts); + inform("Setting direct hosts:" + ir); + proxy.setDirect(ir); + } + + ProxyServer.setProxy(proxy); + } + + /** + * Inits range from the string of semicolon separated ranges. + */ + static InetRange parseInetRange(String source) { + final InetRange irange = new InetRange(); + + final StringTokenizer st = new StringTokenizer(source, ";"); + while (st.hasMoreTokens()) { + irange.add(st.nextToken()); + } + + return irange; + } + + /** + * Integer representaion of the property named name, or -1 if one is not + * found. + */ + static int readInt(Properties props, String name) { + int result = -1; + final String val = (String) props.get(name); + if (val == null) { + return -1; + } + final StringTokenizer st = new StringTokenizer(val); + if (!st.hasMoreElements()) { + return -1; + } + try { + result = Integer.parseInt(st.nextToken()); + } catch (final NumberFormatException nfe) { + inform("Bad value for " + name + ":" + val); + } + return result; + } + + // Display functions + // ///////////////// + + static void inform(String s) { + log.info(s); + } + + static void exit(String msg) { + System.err.println("Error:" + msg); + System.err.println("Aborting operation"); + System.exit(0); + } +} diff --git a/src/com/runjva/sourceforge/jsocks/main/SocksEcho.gif b/src/com/runjva/sourceforge/jsocks/main/SocksEcho.gif new file mode 100644 index 0000000000000000000000000000000000000000..701d39a6fe03e99d9d361076d51aeb6d33a9e972 GIT binary patch literal 926 zcmZ?wbhEHb6krfw_|Cw<0s<{8EWcPsK#ACq&hgk;Vt9&XpHMsT3 yin2xQx}tt?f{Op7Cnq*PaPAbI*k + * This method should return an array {inputstream,outputstream + * [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol allows + * to have method specific encapsulation of data on the socket for purposes + * of integrity or security. And this encapsulation should be performed by + * those streams returned from the method. It is also possible to + * encapsulate datagrams. If authentication method supports such + * encapsulation an instance of the UDPEncapsulation interface should be + * returned as third element of the array, otherwise either null should be + * returned as third element, or array should contain only 2 elements. + * + * @param methodId + * Authentication method selected by the server. + * @param proxySocket + * Socket used to conect to the proxy. + * @return Two or three element array containing Input/Output streams which + * should be used on this connection. Third argument is optional and + * should contain an instance of UDPEncapsulation. It should be + * provided if the authentication method used requires any + * encapsulation to be done on the datagrams. + */ + Object[] doSocksAuthentication(int methodId, java.net.Socket proxySocket) + throws java.io.IOException; +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/AuthenticationNone.java b/src/com/runjva/sourceforge/jsocks/protocol/AuthenticationNone.java new file mode 100644 index 00000000..e6821545 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/AuthenticationNone.java @@ -0,0 +1,22 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * SOCKS5 none authentication. Dummy class does almost nothing. + */ +public class AuthenticationNone implements Authentication { + + public Object[] doSocksAuthentication(final int methodId, + final java.net.Socket proxySocket) throws java.io.IOException { + + if (methodId != 0) { + return null; + } + + InputStream in = proxySocket.getInputStream(); + OutputStream out = proxySocket.getOutputStream(); + return new Object[] { in, out }; + } +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/InetRange.java b/src/com/runjva/sourceforge/jsocks/protocol/InetRange.java new file mode 100644 index 00000000..fae13587 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/InetRange.java @@ -0,0 +1,492 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Class InetRange provides the means of defining the range of inetaddresses. + * It's used by Proxy class to store and look up addresses of machines, that + * should be contacted directly rather then through the proxy. + *

+ * InetRange provides several methods to add either standalone addresses, or + * ranges (e.g. 100.200.300.0:100.200.300.255, which covers all addresses on on + * someones local network). It also provides methods for checking wether given + * address is in this range. Any number of ranges and standalone addresses can + * be added to the range. + */ +public class InetRange implements Cloneable { + + Hashtable host_names; + Vector all; + Vector end_names; + + boolean useSeparateThread = true; + + /** + * Creates the empty range. + */ + public InetRange() { + all = new Vector(); + host_names = new Hashtable(); + end_names = new Vector(); + } + + /** + * Adds another host or range to this range. The String can be one of those: + *

    + *
  • Host name. eg.(Athena.myhost.com or 45.54.56.65) + * + *
  • Range in the form .myhost.net.au
    + * In which case anything that ends with .myhost.net.au will be considered + * in the range. + * + *
  • Range in the form ddd.ddd.ddd.
    + * This will be treated as range ddd.ddd.ddd.0 to ddd.ddd.ddd.255. It is not + * necessary to specify 3 first bytes you can use just one or two. For + * example 130. will cover address between 130.0.0.0 and 13.255.255.255. + * + *
  • Range in the form host_from[: \t\n\r\f]host_to.
    + * That is two hostnames or ips separated by either whitespace or colon. + *
+ */ + public synchronized boolean add(final String s0) { + if (s0 == null) { + return false; + } + + String s = s0.trim(); + if (s.length() == 0) { + return false; + } + + Object[] entry; + + if (s.endsWith(".")) { + // thing like: 111.222.33. + // it is being treated as range 111.222.33.000 - 111.222.33.255 + + final int[] addr = ip2intarray(s); + long from, to; + from = to = 0; + + if (addr == null) { + return false; + } + for (int i = 0; i < 4; i++) { + if (addr[i] >= 0) { + from += (((long) addr[i]) << 8 * (3 - i)); + } else { + to = from; + while (i < 4) { + to += 255l << 8 * (3 - i++); + } + break; + } + } + entry = new Object[] { s, null, new Long(from), new Long(to) }; + all.addElement(entry); + + } else if (s.startsWith(".")) { + // Thing like: .myhost.com + + end_names.addElement(s); + all.addElement(new Object[] { s, null, null, null }); + } else { + final StringTokenizer tokens = new StringTokenizer(s, " \t\r\n\f:"); + if (tokens.countTokens() > 1) { + entry = new Object[] { s, null, null, null }; + resolve(entry, tokens.nextToken(), tokens.nextToken()); + all.addElement(entry); + } else { + entry = new Object[] { s, null, null, null }; + all.addElement(entry); + host_names.put(s, entry); + resolve(entry); + } + + } + + return true; + } + + /** + * Adds another ip for this range. + * + * @param ip + * IP os the host which should be added to this range. + */ + public synchronized void add(final InetAddress ip) { + long from, to; + from = to = ip2long(ip); + all.addElement(new Object[] { ip.getHostName(), ip, new Long(from), + new Long(to) }); + } + + /** + * Adds another range of ips for this range.Any host with ip address greater + * than or equal to the address of from and smaller than or equal to the + * address of to will be included in the range. + * + * @param from + * IP from where range starts(including). + * @param to + * IP where range ends(including). + */ + public synchronized void add(final InetAddress from, final InetAddress to) { + all.addElement(new Object[] { + from.getHostAddress() + ":" + to.getHostAddress(), null, + new Long(ip2long(from)), new Long(ip2long(to)) }); + } + + /** + * Checks wether the givan host is in the range. Attempts to resolve host + * name if required. + * + * @param host + * Host name to check. + * @return true If host is in the range, false otherwise. + * @see InetRange#contains(String,boolean) + */ + public synchronized boolean contains(final String host) { + return contains(host, true); + } + + /** + * Checks wether the given host is in the range. + *

+ * Algorithm:
+ *

    + *
  1. Look up if the hostname is in the range (in the Hashtable). + *
  2. Check if it ends with one of the speciefied endings. + *
  3. Check if it is ip(eg.130.220.35.98). If it is check if it is in the + * range. + *
  4. If attemptResolve is true, host is name, rather than ip, and all + * previous attempts failed, try to resolve the hostname, and check wether + * the ip associated with the host is in the range.It also repeats all + * previos steps with the hostname obtained from InetAddress, but the name + * is not allways the full name,it is quite likely to be the same. Well it + * was on my machine. + *
+ * + * @param host + * Host name to check. + * @param attemptResolve + * Wether to lookup ip address which corresponds to the host,if + * required. + * @return true If host is in the range, false otherwise. + */ + public synchronized boolean contains(final String host0, + final boolean attemptResolve) { + if (all.size() == 0) { + return false; // Empty range + } + + String host = host0.trim(); + if (host.length() == 0) { + return false; + } + + if (checkHost(host)) { + return true; + } + if (checkHostEnding(host)) { + return true; + } + + final long l = host2long(host); + if (l >= 0) { + return contains(l); + } + + if (!attemptResolve) { + return false; + } + + try { + final InetAddress ip = InetAddress.getByName(host); + return contains(ip); + } catch (final UnknownHostException uhe) { + + } + + return false; + } + + /** + * Checks wether the given ip is in the range. + * + * @param ip + * Address of the host to check. + * @return true If host is in the range, false otherwise. + */ + public synchronized boolean contains(final InetAddress ip) { + if (checkHostEnding(ip.getHostName())) { + return true; + } + if (checkHost(ip.getHostName())) { + return true; + } + return contains(ip2long(ip)); + } + + /** + * Get all entries in the range as strings.
+ * These strings can be used to delete entries from the range with remove + * function. + * + * @return Array of entries as strings. + * @see InetRange#remove(String) + */ + public synchronized String[] getAll() { + final int size = all.size(); + Object entry[]; + final String all_names[] = new String[size]; + + for (int i = 0; i < size; ++i) { + entry = all.elementAt(i); + all_names[i] = (String) entry[0]; + } + return all_names; + } + + /** + * Removes an entry from this range.
+ * + * @param s + * Entry to remove. + * @return true if successfull. + */ + public synchronized boolean remove(final String s) { + final Enumeration enumx = all.elements(); + while (enumx.hasMoreElements()) { + final Object[] entry = enumx.nextElement(); + if (s.equals(entry[0])) { + all.removeElement(entry); + end_names.removeElement(s); + host_names.remove(s); + return true; + } + } + return false; + } + + /** Get string representaion of this Range. */ + public String toString() { + final String all[] = getAll(); + if (all.length == 0) { + return ""; + } + + String s = all[0]; + for (int i = 1; i < all.length; ++i) { + s += "; " + all[i]; + } + return s; + } + + /** Creates a clone of this Object */ + + @SuppressWarnings("unchecked") + public Object clone() { + final InetRange new_range = new InetRange(); + new_range.all = (Vector) all.clone(); + new_range.end_names = (Vector) end_names.clone(); + new_range.host_names = (Hashtable) host_names.clone(); + return new_range; + } + + // Private methods + // /////////////// + /** + * Same as previous but used internally, to avoid unnecessary convertion of + * IPs, when checking subranges + */ + private synchronized boolean contains(final long ip) { + final Enumeration enumx = all.elements(); + while (enumx.hasMoreElements()) { + final Object[] obj = enumx.nextElement(); + final Long from = obj[2] == null ? null : (Long) obj[2]; + final Long to = obj[3] == null ? null : (Long) obj[3]; + if ((from != null) && (from.longValue() <= ip) + && (to.longValue() >= ip)) { + return true; + } + + } + return false; + } + + private boolean checkHost(final String host) { + return host_names.containsKey(host); + } + + private boolean checkHostEnding(final String host) { + final Enumeration enumx = end_names.elements(); + while (enumx.hasMoreElements()) { + if (host.endsWith(enumx.nextElement())) { + return true; + } + } + return false; + } + + private void resolve(final Object[] entry) { + // First check if it's in the form ddd.ddd.ddd.ddd. + final long ip = host2long((String) entry[0]); + if (ip >= 0) { + entry[2] = entry[3] = new Long(ip); + } else { + final InetRangeResolver res = new InetRangeResolver(entry); + res.resolve(useSeparateThread); + } + } + + private void resolve(final Object[] entry, final String from, + final String to) { + long f, t; + if (((f = host2long(from)) >= 0) && ((t = host2long(to)) >= 0)) { + entry[2] = new Long(f); + entry[3] = new Long(t); + } else { + final InetRangeResolver res = new InetRangeResolver(entry, from, to); + res.resolve(useSeparateThread); + } + } + + // Class methods + // ///////////// + + // Converts ipv4 to long value(unsigned int) + // ///////////////////////////////////////// + static long ip2long(final InetAddress ip) { + long l = 0; + final byte[] addr = ip.getAddress(); + + if (addr.length == 4) { // IPV4 + for (int i = 0; i < 4; ++i) { + l += (((long) addr[i] & 0xFF) << 8 * (3 - i)); + } + } else { // IPV6 + return 0; // Have no idea how to deal with those + } + return l; + } + + long host2long(final String host) { + long ip = 0; + + // check if it's ddd.ddd.ddd.ddd + if (!Character.isDigit(host.charAt(0))) { + return -1; + } + + final int[] addr = ip2intarray(host); + if (addr == null) { + return -1; + } + + for (int i = 0; i < addr.length; ++i) { + ip += ((long) (addr[i] >= 0 ? addr[i] : 0)) << 8 * (3 - i); + } + + return ip; + } + + static int[] ip2intarray(final String host) { + final int[] address = { -1, -1, -1, -1 }; + int i = 0; + final StringTokenizer tokens = new StringTokenizer(host, "."); + if (tokens.countTokens() > 4) { + return null; + } + while (tokens.hasMoreTokens()) { + try { + address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF; + } catch (final NumberFormatException nfe) { + return null; + } + + } + return address; + } + + /* + * //* This was the test main function //********************************** + * + * public static void main(String args[])throws UnknownHostException{ int i; + * + * InetRange ir = new InetRange(); + * + * + * for(i=0;i + * In order to use it you will need to implement ServerAuthenticator interface. + * There is an implementation of this interface which does no authentication + * ServerAuthenticatorNone, but it is very dangerous to use, as it will give + * access to your local network to anybody in the world. One should never use + * this authentication scheme unless one have pretty good reason to do so. There + * is a couple of other authentication schemes in socks.server package. + * + * @see socks.server.ServerAuthenticator + */ +public class ProxyServer implements Runnable { + + ServerAuthenticator auth; + ProxyMessage msg = null; + + Socket sock = null, remote_sock = null; + ServerSocket ss = null; + UDPRelayServer relayServer = null; + InputStream in, remote_in; + OutputStream out, remote_out; + + int mode; + static final int START_MODE = 0; + static final int ACCEPT_MODE = 1; + static final int PIPE_MODE = 2; + static final int ABORT_MODE = 3; + + static final int BUF_SIZE = 8192; + + Thread pipe_thread1, pipe_thread2; + long lastReadTime; + + static int iddleTimeout = 180000; // 3 minutes + static int acceptTimeout = 180000; // 3 minutes + + static Logger log = LoggerFactory.getLogger(ProxyServer.class); + static SocksProxyBase proxy; + + static VpnService vpnService; + + // Public Constructors + // /////////////////// + + /** + * Creates a proxy server with given Authentication scheme. + * + * @param auth + * Authentication scheme to be used. + */ + public ProxyServer(final ServerAuthenticator auth) { + this.auth = auth; + } + + // Other constructors + // ////////////////// + + ProxyServer(final ServerAuthenticator auth, final Socket s) { + this.auth = auth; + this.sock = s; + this.mode = START_MODE; + } + + // Public methods + // /////////////// + + /** + * Set proxy. + *

+ * Allows Proxy chaining so that one Proxy server is connected to another + * and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests can + * be handled, UDP would not work, however CONNECT and BIND will be + * translated. + * + * @param p + * Proxy which should be used to handle user requests. + */ + public static void setProxy(final SocksProxyBase p) { + proxy = p; + // FIXME: Side effect. + UDPRelayServer.proxy = proxy; + } + + public static void setVpnService (final VpnService v) + { + vpnService = v; + } + /** + * Get proxy. + * + * @return Proxy wich is used to handle user requests. + */ + public static SocksProxyBase getProxy() { + return proxy; + } + + /** + * Sets the timeout for connections, how long shoud server wait for data to + * arrive before dropping the connection.
+ * Zero timeout implies infinity.
+ * Default timeout is 3 minutes. + */ + public static void setIddleTimeout(final int timeout) { + iddleTimeout = timeout; + } + + /** + * Sets the timeout for BIND command, how long the server should wait for + * the incoming connection.
+ * Zero timeout implies infinity.
+ * Default timeout is 3 minutes. + */ + public static void setAcceptTimeout(final int timeout) { + acceptTimeout = timeout; + } + + /** + * Sets the timeout for UDPRelay server.
+ * Zero timeout implies infinity.
+ * Default timeout is 3 minutes. + */ + public static void setUDPTimeout(final int timeout) { + UDPRelayServer.setTimeout(timeout); + } + + /** + * Sets the size of the datagrams used in the UDPRelayServer.
+ * Default size is 64K, a bit more than maximum possible size of the + * datagram. + */ + public static void setDatagramSize(final int size) { + UDPRelayServer.setDatagramSize(size); + } + + /** + * Start the Proxy server at given port.
+ * This methods blocks. + */ + public void start(final int port) { + start(port, 5, null); + } + + /** + * Create a server with the specified port, listen backlog, and local IP + * address to bind to. The localIP argument can be used on a multi-homed + * host for a ServerSocket that will only accept connect requests to one of + * its addresses. If localIP is null, it will default accepting connections + * on any/all local addresses. The port must be between 0 and 65535, + * inclusive.
+ * This methods blocks. + */ + public void start(final int port, final int backlog, + final InetAddress localIP) { + try { + ss = new ServerSocket(port, backlog, localIP); + final String address = ss.getInetAddress().getHostAddress(); + final int localPort = ss.getLocalPort(); + log.info("Starting SOCKS Proxy on: {}:{}", address, localPort); + + while (true) { + final Socket s = ss.accept(); + final String hostName = s.getInetAddress().getHostName(); + final int port2 = s.getPort(); + log.info("Accepted from:{}:{}", hostName, port2); + + final ProxyServer ps = new ProxyServer(auth, s); + (new Thread(ps)).start(); + } + } catch (final IOException ioe) { + ioe.printStackTrace(); + } finally { + } + } + + /** + * Stop server operation.It would be wise to interrupt thread running the + * server afterwards. + */ + public void stop() { + try { + if (ss != null) { + ss.close(); + } + } catch (final IOException ioe) { + } + } + + // Runnable interface + // ////////////////// + public void run() { + switch (mode) { + case START_MODE: + try { + startSession(); + } catch (final IOException ioe) { + handleException(ioe); + // ioe.printStackTrace(); + } finally { + abort(); + if (auth != null) { + auth.endSession(); + } + log.info("Main thread(client->remote)stopped."); + } + break; + case ACCEPT_MODE: + try { + doAccept(); + mode = PIPE_MODE; + pipe_thread1.interrupt(); // Tell other thread that connection + // have + // been accepted. + pipe(remote_in, out); + } catch (final IOException ioe) { + // log("Accept exception:"+ioe); + handleException(ioe); + } finally { + abort(); + log.info("Accept thread(remote->client) stopped"); + } + break; + case PIPE_MODE: + try { + pipe(remote_in, out); + } catch (final IOException ioe) { + } finally { + abort(); + log.info("Support thread(remote->client) stopped"); + } + break; + case ABORT_MODE: + break; + default: + log.warn("Unexpected MODE " + mode); + } + } + + // Private methods + // /////////////// + private void startSession() throws IOException { + sock.setSoTimeout(iddleTimeout); + + try { + auth = auth.startSession(sock); + } catch (final IOException ioe) { + log.warn("Auth throwed exception:", ioe); + auth = null; + return; + } + + if (auth == null) { // Authentication failed + log.info("Authentication failed"); + return; + } + + in = auth.getInputStream(); + out = auth.getOutputStream(); + + msg = readMsg(in); + handleRequest(msg); + } + + private void handleRequest(final ProxyMessage msg) throws IOException { + if (!auth.checkRequest(msg)) { + throw new SocksException(SocksProxyBase.SOCKS_FAILURE); + } + + if (msg.ip == null) { + if (msg instanceof Socks5Message) { + msg.ip = InetAddress.getByName(msg.host); + } else { + throw new SocksException(SocksProxyBase.SOCKS_FAILURE); + } + } + log(msg); + + switch (msg.command) { + case SocksProxyBase.SOCKS_CMD_CONNECT: + onConnect(msg); + break; + case SocksProxyBase.SOCKS_CMD_BIND: + onBind(msg); + break; + case SocksProxyBase.SOCKS_CMD_UDP_ASSOCIATE: + onUDP(msg); + break; + default: + throw new SocksException(SocksProxyBase.SOCKS_CMD_NOT_SUPPORTED); + } + } + + private void handleException(final IOException ioe) { + // If we couldn't read the request, return; + if (msg == null) { + return; + } + // If have been aborted by other thread + if (mode == ABORT_MODE) { + return; + } + // If the request was successfully completed, but exception happened + // later + if (mode == PIPE_MODE) { + return; + } + + int error_code = SocksProxyBase.SOCKS_FAILURE; + + if (ioe instanceof SocksException) { + error_code = ((SocksException) ioe).errCode; + } else if (ioe instanceof NoRouteToHostException) { + error_code = SocksProxyBase.SOCKS_HOST_UNREACHABLE; + } else if (ioe instanceof ConnectException) { + error_code = SocksProxyBase.SOCKS_CONNECTION_REFUSED; + } else if (ioe instanceof InterruptedIOException) { + error_code = SocksProxyBase.SOCKS_TTL_EXPIRE; + } + + if ((error_code > SocksProxyBase.SOCKS_ADDR_NOT_SUPPORTED) + || (error_code < 0)) { + error_code = SocksProxyBase.SOCKS_FAILURE; + } + + sendErrorMessage(error_code); + } + + private void onConnect(final ProxyMessage msg) throws IOException { + Socket s; + + if (proxy == null) { + //s = new Socket(msg.ip, msg.port); + + s= SocketChannel.open().socket(); + if ((null != s) && (null != vpnService)) { + vpnService.protect(s); + } + + s.connect(new InetSocketAddress(msg.ip,msg.port)); + + } else { + s = new SocksSocket(proxy, msg.ip, msg.port); + } + + if (vpnService != null) + vpnService.protect(s); + + log.info("Connected to " + s.getInetAddress() + ":" + s.getPort()); + + ProxyMessage response = null; + final InetAddress localAddress = s.getLocalAddress(); + final int localPort = s.getLocalPort(); + + if (msg instanceof Socks5Message) { + final int cmd = SocksProxyBase.SOCKS_SUCCESS; + response = new Socks5Message(cmd, localAddress, localPort); + } else { + final int cmd = Socks4Message.REPLY_OK; + response = new Socks4Message(cmd, localAddress, localPort); + + } + response.write(out); + startPipe(s); + } + + private void onBind(final ProxyMessage msg) throws IOException { + ProxyMessage response = null; + + if (proxy == null) { + ss = new ServerSocket(0); + } else { + ss = new SocksServerSocket(proxy, msg.ip, msg.port); + } + + ss.setSoTimeout(acceptTimeout); + + final InetAddress inetAddress = ss.getInetAddress(); + final int localPort = ss.getLocalPort(); + log.info("Trying accept on {}:{}", inetAddress, localPort); + + if (msg.version == 5) { + final int cmd = SocksProxyBase.SOCKS_SUCCESS; + response = new Socks5Message(cmd, inetAddress, localPort); + } else { + final int cmd = Socks4Message.REPLY_OK; + response = new Socks4Message(cmd, inetAddress, localPort); + } + response.write(out); + + mode = ACCEPT_MODE; + + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + + // Make timeout infinit. + sock.setSoTimeout(0); + int eof = 0; + + try { + while ((eof = in.read()) >= 0) { + if (mode != ACCEPT_MODE) { + if (mode != PIPE_MODE) { + return;// Accept failed + } + + remote_out.write(eof); + break; + } + } + } catch (final EOFException e) { + log.debug("Connection closed while we were trying to accept", e); + return; + } catch (final InterruptedIOException e) { + log.debug("Interrupted by unsucessful accept thread", e); + if (mode != PIPE_MODE) { + return; + } + } finally { + // System.out.println("Finnaly!"); + } + + if (eof < 0) { + return; + } + + // Do not restore timeout, instead timeout is set on the + // remote socket. It does not make any difference. + + pipe(in, remote_out); + } + + private void onUDP(final ProxyMessage msg) throws IOException { + if (msg.ip.getHostAddress().equals("0.0.0.0")) { + msg.ip = sock.getInetAddress(); + } + log.info("Creating UDP relay server for {}:{}", msg.ip, msg.port); + + relayServer = new UDPRelayServer(msg.ip, msg.port, + Thread.currentThread(), sock, auth); + + ProxyMessage response; + + response = new Socks5Message(SocksProxyBase.SOCKS_SUCCESS, + relayServer.relayIP, relayServer.relayPort); + + response.write(out); + + relayServer.start(); + + // Make timeout infinit. + sock.setSoTimeout(0); + try { + while (in.read() >= 0) { + /* do nothing */; + // FIXME: Consider a slight delay here? + } + } catch (final EOFException eofe) { + } + } + + // Private methods + // //////////////// + + private void doAccept() throws IOException { + Socket s = null; + final long startTime = System.currentTimeMillis(); + + while (true) { + s = ss.accept(); + if (s.getInetAddress().equals(msg.ip)) { + // got the connection from the right host + // Close listenning socket. + ss.close(); + break; + } else if (ss instanceof SocksServerSocket) { + // We can't accept more then one connection + s.close(); + ss.close(); + throw new SocksException(SocksProxyBase.SOCKS_FAILURE); + } else { + if (acceptTimeout != 0) { // If timeout is not infinit + final long passed = System.currentTimeMillis() - startTime; + final int newTimeout = acceptTimeout - (int) passed; + + if (newTimeout <= 0) { + throw new InterruptedIOException("newTimeout <= 0"); + } + ss.setSoTimeout(newTimeout); + } + s.close(); // Drop all connections from other hosts + } + } + + // Accepted connection + remote_sock = s; + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + + // Set timeout + remote_sock.setSoTimeout(iddleTimeout); + + final InetAddress inetAddress = s.getInetAddress(); + final int port = s.getPort(); + log.info("Accepted from {}:{}", s.getInetAddress(), port); + + ProxyMessage response; + + if (msg.version == 5) { + final int cmd = SocksProxyBase.SOCKS_SUCCESS; + response = new Socks5Message(cmd, inetAddress, port); + } else { + final int cmd = Socks4Message.REPLY_OK; + response = new Socks4Message(cmd, inetAddress, port); + } + response.write(out); + } + + private ProxyMessage readMsg(final InputStream in) throws IOException { + PushbackInputStream push_in; + if (in instanceof PushbackInputStream) { + push_in = (PushbackInputStream) in; + } else { + push_in = new PushbackInputStream(in); + } + + final int version = push_in.read(); + push_in.unread(version); + + ProxyMessage msg; + + if (version == 5) { + msg = new Socks5Message(push_in, false); + } else if (version == 4) { + msg = new Socks4Message(push_in, false); + } else { + throw new SocksException(SocksProxyBase.SOCKS_FAILURE); + } + return msg; + } + + private void startPipe(final Socket s) { + mode = PIPE_MODE; + remote_sock = s; + try { + remote_in = s.getInputStream(); + remote_out = s.getOutputStream(); + pipe_thread1 = Thread.currentThread(); + pipe_thread2 = new Thread(this); + pipe_thread2.start(); + pipe(in, remote_out); + } catch (final IOException ioe) { + } + } + + private void sendErrorMessage(final int error_code) { + ProxyMessage err_msg; + if (msg instanceof Socks4Message) { + err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED); + } else { + err_msg = new Socks5Message(error_code); + } + try { + err_msg.write(out); + } catch (final IOException ioe) { + } + } + + private synchronized void abort() { + if (mode == ABORT_MODE) { + return; + } + mode = ABORT_MODE; + try { + log.info("Aborting operation"); + if (remote_sock != null) { + remote_sock.close(); + } + if (sock != null) { + sock.close(); + } + if (relayServer != null) { + relayServer.stop(); + } + if (ss != null) { + ss.close(); + } + if (pipe_thread1 != null) { + pipe_thread1.interrupt(); + } + if (pipe_thread2 != null) { + pipe_thread2.interrupt(); + } + } catch (final IOException ioe) { + } + } + + static final void log(final ProxyMessage msg) { + log.debug("Request version: {}, Command: ", msg.version, + command2String(msg.command)); + + final String user = msg.version == 4 ? ", User:" + msg.user : ""; + log.debug("IP:" + msg.ip + ", Port:" + msg.port + user); + } + + private void pipe(final InputStream in, final OutputStream out) + throws IOException { + lastReadTime = System.currentTimeMillis(); + final byte[] buf = new byte[BUF_SIZE]; + int len = 0; + while (len >= 0) { + try { + if (len != 0) { + out.write(buf, 0, len); + out.flush(); + } + len = in.read(buf); + lastReadTime = System.currentTimeMillis(); + } catch (final InterruptedIOException iioe) { + if (iddleTimeout == 0) { + return;// Other thread interrupted us. + } + final long timeSinceRead = System.currentTimeMillis() + - lastReadTime; + + if (timeSinceRead >= iddleTimeout - 1000) { + return; + } + len = 0; + + } + } + } + + static final String command_names[] = { "CONNECT", "BIND", "UDP_ASSOCIATE" }; + + static final String command2String(int cmd) { + if ((cmd > 0) && (cmd < 4)) { + return command_names[cmd - 1]; + } else { + return "Unknown Command " + cmd; + } + } +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/Socks4Message.java b/src/com/runjva/sourceforge/jsocks/protocol/Socks4Message.java new file mode 100644 index 00000000..484ad969 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/Socks4Message.java @@ -0,0 +1,167 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * SOCKS4 Reply/Request message. + */ + +class Socks4Message extends ProxyMessage { + + private byte[] msgBytes; + private int msgLength; + + /** + * Server failed reply, cmd command for failed request + */ + public Socks4Message(final int cmd) { + super(cmd, null, 0); + this.user = null; + + msgLength = 2; + msgBytes = new byte[2]; + + msgBytes[0] = (byte) 0; + msgBytes[1] = (byte) command; + } + + /** + * Server successfull reply + */ + public Socks4Message(final int cmd, final InetAddress ip, final int port) { + this(0, cmd, ip, port, null); + } + + /** + * Client request + */ + public Socks4Message(final int cmd, final InetAddress ip, final int port, + final String user) { + this(SOCKS_VERSION, cmd, ip, port, user); + } + + /** + * Most general constructor + */ + public Socks4Message(final int version, final int cmd, + final InetAddress ip, final int port, final String user) { + + super(cmd, ip, port); + this.user = user; + this.version = version; + + msgLength = user == null ? 8 : 9 + user.length(); + msgBytes = new byte[msgLength]; + + msgBytes[0] = (byte) version; + msgBytes[1] = (byte) command; + msgBytes[2] = (byte) (port >> 8); + msgBytes[3] = (byte) port; + + byte[] addr; + + if (ip != null) { + addr = ip.getAddress(); + } else { + addr = new byte[4]; + addr[0] = addr[1] = addr[2] = addr[3] = 0; + } + System.arraycopy(addr, 0, msgBytes, 4, 4); + + if (user != null) { + final byte[] buf = user.getBytes(); + System.arraycopy(buf, 0, msgBytes, 8, buf.length); + msgBytes[msgBytes.length - 1] = 0; + } + } + + /** + * Initialise from the stream If clientMode is true attempts to read a + * server response otherwise reads a client request see read for more detail + */ + public Socks4Message(final InputStream in, final boolean clientMode) + throws IOException { + msgBytes = null; + read(in, clientMode); + } + + public void read(final InputStream in) throws IOException { + read(in, true); + } + + public void read(final InputStream in, final boolean clientMode) + throws IOException { + final DataInputStream d_in = new DataInputStream(in); + version = d_in.readUnsignedByte(); + command = d_in.readUnsignedByte(); + if (clientMode && (command != REPLY_OK)) { + String errMsg; + // FIXME: Range should be replaced with cases. + if ((command > REPLY_OK) && (command < REPLY_BAD_IDENTD)) { + errMsg = replyMessage[command - REPLY_OK]; + } else { + errMsg = "Unknown Reply Code"; + } + throw new SocksException(command, errMsg); + } + port = d_in.readUnsignedShort(); + final byte[] addr = new byte[4]; + d_in.readFully(addr); + ip = bytes2IP(addr); + host = ip.getHostName(); + if (!clientMode) { + int b = in.read(); + // FIXME: Hope there are no idiots with user name bigger than this + final byte[] userBytes = new byte[256]; + int i = 0; + for (i = 0; (i < userBytes.length) && (b > 0); ++i) { + userBytes[i] = (byte) b; + b = in.read(); + } + user = new String(userBytes, 0, i); + } + } + + public void write(final OutputStream out) throws IOException { + if (msgBytes == null) { + final Socks4Message msg; + msg = new Socks4Message(version, command, ip, port, user); + msgBytes = msg.msgBytes; + msgLength = msg.msgLength; + } + out.write(msgBytes); + } + + // Class methods + static InetAddress bytes2IP(final byte[] addr) { + final String s = bytes2IPV4(addr, 0); + try { + return InetAddress.getByName(s); + } catch (final UnknownHostException uh_ex) { + return null; + } + } + + // Constants + + static final String[] replyMessage = { "Request Granted", + "Request Rejected or Failed", + "Failed request, can't connect to Identd", + "Failed request, bad user name" }; + + static final int SOCKS_VERSION = 4; + + public final static int REQUEST_CONNECT = 1; + public final static int REQUEST_BIND = 2; + + public final static int REPLY_OK = 90; + public final static int REPLY_REJECTED = 91; + public final static int REPLY_NO_CONNECT = 92; + public final static int REPLY_BAD_IDENTD = 93; + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/Socks4Proxy.java b/src/com/runjva/sourceforge/jsocks/protocol/Socks4Proxy.java new file mode 100644 index 00000000..ac4363bb --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/Socks4Proxy.java @@ -0,0 +1,144 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Proxy which describes SOCKS4 proxy. + */ + +public class Socks4Proxy extends SocksProxyBase implements Cloneable { + + // Data members + String user; + + // Public Constructors + // ==================== + + /** + * Creates the SOCKS4 proxy + * + * @param p + * Proxy to use to connect to this proxy, allows proxy chaining. + * @param proxyHost + * Address of the proxy server. + * @param proxyPort + * Port of the proxy server + * @param user + * User name to use for identification purposes. + * @throws UnknownHostException + * If proxyHost can't be resolved. + */ + public Socks4Proxy(SocksProxyBase p, String proxyHost, int proxyPort, + String user) throws UnknownHostException { + super(p, proxyHost, proxyPort); + this.user = new String(user); + version = 4; + } + + /** + * Creates the SOCKS4 proxy + * + * @param proxyHost + * Address of the proxy server. + * @param proxyPort + * Port of the proxy server + * @param user + * User name to use for identification purposes. + * @throws UnknownHostException + * If proxyHost can't be resolved. + */ + public Socks4Proxy(String proxyHost, int proxyPort, String user) + throws UnknownHostException { + this(null, proxyHost, proxyPort, user); + } + + /** + * Creates the SOCKS4 proxy + * + * @param p + * Proxy to use to connect to this proxy, allows proxy chaining. + * @param proxyIP + * Address of the proxy server. + * @param proxyPort + * Port of the proxy server + * @param user + * User name to use for identification purposes. + */ + public Socks4Proxy(SocksProxyBase p, InetAddress proxyIP, int proxyPort, + String user) { + super(p, proxyIP, proxyPort); + this.user = new String(user); + version = 4; + } + + /** + * Creates the SOCKS4 proxy + * + * @param proxyIP + * Address of the proxy server. + * @param proxyPort + * Port of the proxy server + * @param user + * User name to use for identification purposes. + */ + public Socks4Proxy(InetAddress proxyIP, int proxyPort, String user) { + this(null, proxyIP, proxyPort, user); + } + + // Public instance methods + // ======================== + + /** + * Creates a clone of this proxy. Changes made to the clone should not + * affect this object. + */ + public Object clone() { + final Socks4Proxy newProxy = new Socks4Proxy(proxyIP, proxyPort, user); + newProxy.directHosts = (InetRange) directHosts.clone(); + newProxy.chainProxy = chainProxy; + return newProxy; + } + + // Public Static(Class) Methods + // ============================== + + // Protected Methods + // ================= + + protected SocksProxyBase copy() { + final Socks4Proxy copy = new Socks4Proxy(proxyIP, proxyPort, user); + copy.directHosts = this.directHosts; + copy.chainProxy = chainProxy; + return copy; + } + + protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) { + switch (cmd) { + case SOCKS_CMD_CONNECT: + cmd = Socks4Message.REQUEST_CONNECT; + break; + case SOCKS_CMD_BIND: + cmd = Socks4Message.REQUEST_BIND; + break; + default: + return null; + } + return new Socks4Message(cmd, ip, port, user); + } + + protected ProxyMessage formMessage(int cmd, String host, int port) + throws UnknownHostException { + + return formMessage(cmd, InetAddress.getByName(host), port); + } + + protected ProxyMessage formMessage(InputStream in) throws SocksException, + IOException { + + return new Socks4Message(in, true); + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/Socks5DatagramSocket.java b/src/com/runjva/sourceforge/jsocks/protocol/Socks5DatagramSocket.java new file mode 100644 index 00000000..6dcaf44c --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/Socks5DatagramSocket.java @@ -0,0 +1,485 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Datagram socket to interract through the firewall.
+ * Can be used same way as the normal DatagramSocket. One should be carefull + * though with the datagram sizes used, as additional data is present in both + * incomming and outgoing datagrams. + *

+ * SOCKS5 protocol allows to send host address as either: + *

    + *
  • IPV4, normal 4 byte address. (10 bytes header size) + *
  • IPV6, version 6 ip address (not supported by Java as for now). 22 bytes + * header size. + *
  • Host name,(7+length of the host name bytes header size). + *
+ * As with other Socks equivalents, direct addresses are handled transparently, + * that is data will be send directly when required by the proxy settings. + *

+ * NOTE:
+ * Unlike other SOCKS Sockets, it does not support proxy chaining, and + * will throw an exception if proxy has a chain proxy attached. The reason for + * that is not my laziness, but rather the restrictions of the SOCKSv5 protocol. + * Basicaly SOCKSv5 proxy server, needs to know from which host:port datagrams + * will be send for association, and returns address to which datagrams should + * be send by the client, but it does not inform client from which host:port it + * is going to send datagrams, in fact there is even no guarantee they will be + * send at all and from the same address each time. + */ +public class Socks5DatagramSocket extends DatagramSocket { + + InetAddress relayIP; + int relayPort; + Socks5Proxy proxy; + private boolean server_mode = false; + UDPEncapsulation encapsulation; + + private Logger log = LoggerFactory.getLogger(Socks5DatagramSocket.class); + + /** + * Construct Datagram socket for communication over SOCKS5 proxy server. + * This constructor uses default proxy, the one set with + * Proxy.setDefaultProxy() method. If default proxy is not set or it is set + * to version4 proxy, which does not support datagram forwarding, throws + * SocksException. + */ + public Socks5DatagramSocket() throws SocksException, IOException { + this(SocksProxyBase.defaultProxy, 0, null); + } + + /** + * Construct Datagram socket for communication over SOCKS5 proxy server. And + * binds it to the specified local port. This constructor uses default + * proxy, the one set with Proxy.setDefaultProxy() method. If default proxy + * is not set or it is set to version4 proxy, which does not support + * datagram forwarding, throws SocksException. + */ + public Socks5DatagramSocket(int port) throws SocksException, IOException { + this(SocksProxyBase.defaultProxy, port, null); + } + + /** + * Construct Datagram socket for communication over SOCKS5 proxy server. And + * binds it to the specified local port and address. This constructor uses + * default proxy, the one set with Proxy.setDefaultProxy() method. If + * default proxy is not set or it is set to version4 proxy, which does not + * support datagram forwarding, throws SocksException. + */ + public Socks5DatagramSocket(int port, InetAddress ip) + throws SocksException, IOException { + this(SocksProxyBase.defaultProxy, port, ip); + } + + /** + * Constructs datagram socket for communication over specified proxy. And + * binds it to the given local address and port. Address of null and port of + * 0, signify any availabale port/address. Might throw SocksException, if: + *

    + *
  1. Given version of proxy does not support UDP_ASSOCIATE. + *
  2. Proxy can't be reached. + *
  3. Authorization fails. + *
  4. Proxy does not want to perform udp forwarding, for any reason. + *
+ * Might throw IOException if binding datagram socket to given address/port + * fails. See java.net.DatagramSocket for more details. + */ + public Socks5DatagramSocket(SocksProxyBase p, int port, InetAddress ip) + throws SocksException, IOException { + + super(port, ip); + + if (p == null) { + throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY); + } + + if (!(p instanceof Socks5Proxy)) { + final String s = "Datagram Socket needs Proxy version 5"; + throw new SocksException(-1, s); + } + + if (p.chainProxy != null) { + final String s = "Datagram Sockets do not support proxy chaining."; + throw new SocksException(SocksProxyBase.SOCKS_JUST_ERROR, s); + } + + proxy = (Socks5Proxy) p.copy(); + + final ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(), + super.getLocalPort()); + + relayIP = msg.ip; + if (relayIP.getHostAddress().equals("0.0.0.0")) { + // FIXME: What happens here? + relayIP = proxy.proxyIP; + } + relayPort = msg.port; + + encapsulation = proxy.udp_encapsulation; + + log.debug("Datagram Socket:{}:{}", getLocalAddress(), getLocalPort()); + log.debug("Socks5Datagram: {}:{}", relayIP, relayPort); + } + + /** + * Used by UDPRelayServer. + */ + Socks5DatagramSocket(boolean server_mode, UDPEncapsulation encapsulation, + InetAddress relayIP, int relayPort) throws IOException { + super(); + this.server_mode = server_mode; + this.relayIP = relayIP; + this.relayPort = relayPort; + this.encapsulation = encapsulation; + this.proxy = null; + } + + /** + * Sends the Datagram either through the proxy or directly depending on + * current proxy settings and destination address.
+ * + * NOTE: DatagramPacket size should be at least 10 bytes less than + * the systems limit. + * + *

+ * See documentation on java.net.DatagramSocket for full details on how to + * use this method. + * + * @param dp + * Datagram to send. + * @throws IOException + * If error happens with I/O. + */ + public void send(DatagramPacket dp) throws IOException { + // If the host should be accessed directly, send it as is. + if (!server_mode && proxy.isDirect(dp.getAddress())) { + super.send(dp); + log.debug("Sending datagram packet directly:"); + return; + } + + final byte[] head = formHeader(dp.getAddress(), dp.getPort()); + byte[] buf = new byte[head.length + dp.getLength()]; + final byte[] data = dp.getData(); + + // Merge head and data + System.arraycopy(head, 0, buf, 0, head.length); + // System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); + System.arraycopy(data, 0, buf, head.length, dp.getLength()); + + if (encapsulation != null) { + buf = encapsulation.udpEncapsulate(buf, true); + } + + super.send(new DatagramPacket(buf, buf.length, relayIP, relayPort)); + } + + /** + * This method allows to send datagram packets with address type DOMAINNAME. + * SOCKS5 allows to specify host as names rather than ip addresses.Using + * this method one can send udp datagrams through the proxy, without having + * to know the ip address of the destination host. + *

+ * If proxy specified for that socket has an option resolveAddrLocally set + * to true host will be resolved, and the datagram will be send with address + * type IPV4, if resolve fails, UnknownHostException is thrown. + * + * @param dp + * Datagram to send, it should contain valid port and data + * @param host + * Host name to which datagram should be send. + * @throws IOException + * If error happens with I/O, or the host can't be resolved when + * proxy settings say that hosts should be resolved locally. + * @see Socks5Proxy#resolveAddrLocally(boolean) + */ + public void send(DatagramPacket dp, String host) throws IOException { + if (proxy.isDirect(host)) { + dp.setAddress(InetAddress.getByName(host)); + super.send(dp); + return; + } + + if ((proxy).resolveAddrLocally) { + dp.setAddress(InetAddress.getByName(host)); + } + + final byte[] head = formHeader(host, dp.getPort()); + byte[] buf = new byte[head.length + dp.getLength()]; + final byte[] data = dp.getData(); + // Merge head and data + System.arraycopy(head, 0, buf, 0, head.length); + // System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength()); + System.arraycopy(data, 0, buf, head.length, dp.getLength()); + + if (encapsulation != null) { + buf = encapsulation.udpEncapsulate(buf, true); + } + + super.send(new DatagramPacket(buf, buf.length, relayIP, relayPort)); + } + + /** + * Receives udp packet. If packet have arrived from the proxy relay server, + * it is processed and address and port of the packet are set to the address + * and port of sending host.
+ * If the packet arrived from anywhere else it is not changed.
+ * NOTE: DatagramPacket size should be at least 10 bytes bigger + * than the largest packet you expect (this is for IPV4 addresses). For + * hostnames and IPV6 it is even more. + * + * @param dp + * Datagram in which all relevent information will be copied. + */ + public void receive(DatagramPacket dp) throws IOException { + super.receive(dp); + + if (server_mode) { + // Drop all datagrams not from relayIP/relayPort + final int init_length = dp.getLength(); + final int initTimeout = getSoTimeout(); + final long startTime = System.currentTimeMillis(); + + while (!relayIP.equals(dp.getAddress()) + || (relayPort != dp.getPort())) { + + // Restore datagram size + dp.setLength(init_length); + + // If there is a non-infinit timeout on this socket + // Make sure that it happens no matter how often unexpected + // packets arrive. + if (initTimeout != 0) { + final long passed = System.currentTimeMillis() - startTime; + final int newTimeout = initTimeout - (int) passed; + + if (newTimeout <= 0) { + throw new InterruptedIOException( + "In Socks5DatagramSocket->receive()"); + } + setSoTimeout(newTimeout); + } + + super.receive(dp); + } + + // Restore timeout settings + if (initTimeout != 0) { + setSoTimeout(initTimeout); + } + + } else if (!relayIP.equals(dp.getAddress()) + || (relayPort != dp.getPort())) { + return; // Recieved direct packet + // If the datagram is not from the relay server, return it it as is. + } + + byte[] data; + data = dp.getData(); + + if (encapsulation != null) { + data = encapsulation.udpEncapsulate(data, false); + } + + // FIXME: What is this? + final int offset = 0; // Java 1.1 + // int offset = dp.getOffset(); //Java 1.2 + + final ByteArrayInputStream bIn = new ByteArrayInputStream(data, offset, + dp.getLength()); + + final ProxyMessage msg = new Socks5Message(bIn); + dp.setPort(msg.port); + dp.setAddress(msg.getInetAddress()); + + // what wasn't read by the Message is the data + final int data_length = bIn.available(); + // Shift data to the left + System.arraycopy(data, offset + dp.getLength() - data_length, data, + offset, data_length); + + dp.setLength(data_length); + } + + /** + * Returns port assigned by the proxy, to which datagrams are relayed. It is + * not the same port to which other party should send datagrams. + * + * @return Port assigned by socks server to which datagrams are send for + * association. + */ + public int getLocalPort() { + if (server_mode) { + return super.getLocalPort(); + } + return relayPort; + } + + /** + * Address assigned by the proxy, to which datagrams are send for relay. It + * is not necesseraly the same address, to which other party should send + * datagrams. + * + * @return Address to which datagrams are send for association. + */ + public InetAddress getLocalAddress() { + if (server_mode) { + return super.getLocalAddress(); + } + return relayIP; + } + + /** + * Closes datagram socket, and proxy connection. + */ + public void close() { + if (!server_mode) { + proxy.endSession(); + } + super.close(); + } + + /** + * This method checks wether proxy still runs udp forwarding service for + * this socket. + *

+ * This methods checks wether the primary connection to proxy server is + * active. If it is, chances are that proxy continues to forward datagrams + * being send from this socket. If it was closed, most likely datagrams are + * no longer being forwarded by the server. + *

+ * Proxy might decide to stop forwarding datagrams, in which case it should + * close primary connection. This method allows to check, wether this have + * been done. + *

+ * You can specify timeout for which we should be checking EOF condition on + * the primary connection. Timeout is in milliseconds. Specifying 0 as + * timeout implies infinity, in which case method will block, until + * connection to proxy is closed or an error happens, and then return false. + *

+ * One possible scenario is to call isProxyactive(0) in separate thread, and + * once it returned notify other threads about this event. + * + * @param timeout + * For how long this method should block, before returning. + * @return true if connection to proxy is active, false if eof or error + * condition have been encountered on the connection. + */ + public boolean isProxyAlive(int timeout) { + if (server_mode) { + return false; + } + if (proxy != null) { + try { + proxy.proxySocket.setSoTimeout(timeout); + + final int eof = proxy.in.read(); + if (eof < 0) { + return false; // EOF encountered. + } else { + log.warn("This really should not happen"); + return true; // This really should not happen + } + + } catch (final InterruptedIOException iioe) { + return true; // read timed out. + } catch (final IOException ioe) { + return false; + } + } + return false; + } + + // PRIVATE METHODS + // //////////////// + + private byte[] formHeader(InetAddress ip, int port) { + final Socks5Message request = new Socks5Message(0, ip, port); + request.data[0] = 0; + return request.data; + } + + private byte[] formHeader(String host, int port) { + final Socks5Message request = new Socks5Message(0, host, port); + request.data[0] = 0; + return request.data; + } + + /* + * ====================================================================== + * + * //Mainly Test functions ////////////////////// + * + * private String bytes2String(byte[] b){ String s=""; char[] hex_digit = { + * '0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'}; + * for(int i=0;i> 4; int i2 = b[i] & + * 0xF; s+=hex_digit[i1]; s+=hex_digit[i2]; s+=" "; } return s; } private + * static final void debug(String s){ if(DEBUG) System.out.print(s); } + * + * private static final boolean DEBUG = true; + * + * + * public static void usage(){ System.err.print( + * "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n" + * ); } + * + * static final int defaultProxyPort = 1080; //Default Port static final + * String defaultProxyHost = "www-proxy"; //Default proxy + * + * public static void main(String args[]){ int port; String host; int + * proxyPort; String proxyHost; InetAddress ip; + * + * if(args.length > 1 && args.length < 5){ try{ + * + * host = args[0]; port = Integer.parseInt(args[1]); + * + * proxyPort =(args.length > 3)? Integer.parseInt(args[3]) : + * defaultProxyPort; + * + * host = args[0]; ip = InetAddress.getByName(host); + * + * proxyHost =(args.length > 2)? args[2] : defaultProxyHost; + * + * Proxy.setDefaultProxy(proxyHost,proxyPort); Proxy p = + * Proxy.getDefaultProxy(); p.addDirect("lux"); + * + * + * DatagramSocket ds = new Socks5DatagramSocket(); + * + * + * BufferedReader in = new BufferedReader( new + * InputStreamReader(System.in)); String s; + * + * System.out.print("Enter line:"); s = in.readLine(); + * + * while(s != null){ byte[] data = (s+"\r\n").getBytes(); DatagramPacket dp + * = new DatagramPacket(data,0,data.length, ip,port); + * System.out.println("Sending to: "+ip+":"+port); ds.send(dp); dp = new + * DatagramPacket(new byte[1024],1024); + * + * System.out.println("Trying to recieve on port:"+ ds.getLocalPort()); + * ds.receive(dp); System.out.print("Recieved:\n"+ + * "From:"+dp.getAddress()+":"+dp.getPort()+ "\n\n"+ new + * String(dp.getData(),dp.getOffset(),dp.getLength())+"\n" ); + * System.out.print("Enter line:"); s = in.readLine(); + * + * } ds.close(); System.exit(1); + * + * }catch(SocksException s_ex){ System.err.println("SocksException:"+s_ex); + * s_ex.printStackTrace(); System.exit(1); }catch(IOException io_ex){ + * io_ex.printStackTrace(); System.exit(1); }catch(NumberFormatException + * num_ex){ usage(); num_ex.printStackTrace(); System.exit(1); } + * + * }else{ usage(); } } + */ + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/Socks5Message.java b/src/com/runjva/sourceforge/jsocks/protocol/Socks5Message.java new file mode 100644 index 00000000..0f24715b --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/Socks5Message.java @@ -0,0 +1,330 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SOCKS5 request/response message. + */ + +class Socks5Message extends ProxyMessage { + /** Address type of given message */ + public int addrType; + + byte[] data; + + private Logger log = LoggerFactory.getLogger(Socks5Message.class); + + /** + * Server error response. + * + * @param cmd + * Error code. + */ + public Socks5Message(int cmd) { + super(cmd, null, 0); + data = new byte[3]; + data[0] = SOCKS_VERSION; // Version. + data[1] = (byte) cmd; // Reply code for some kind of failure. + data[2] = 0; // Reserved byte. + } + + /** + * Construct client request or server response. + * + * @param cmd + * - Request/Response code. + * @param ip + * - IP field. + * @paarm port - port field. + */ + public Socks5Message(int cmd, InetAddress ip, int port) { + super(cmd, ip, port); + + if (ip == null) { + this.host = "0.0.0.0"; + } else { + this.host = ip.getHostName(); + } + + this.version = SOCKS_VERSION; + + byte[] addr; + + if (ip == null) { + addr = new byte[4]; + addr[0] = addr[1] = addr[2] = addr[3] = 0; + } else { + addr = ip.getAddress(); + } + + if (addr.length == 4) { + addrType = SOCKS_ATYP_IPV4; + } else { + addrType = SOCKS_ATYP_IPV6; + } + + data = new byte[6 + addr.length]; + data[0] = (byte) SOCKS_VERSION; // Version + data[1] = (byte) command; // Command + data[2] = (byte) 0; // Reserved byte + data[3] = (byte) addrType; // Address type + + // Put Address + System.arraycopy(addr, 0, data, 4, addr.length); + // Put port + data[data.length - 2] = (byte) (port >> 8); + data[data.length - 1] = (byte) (port); + } + + /** + * Construct client request or server response. + * + * @param cmd + * - Request/Response code. + * @param hostName + * - IP field as hostName, uses ADDR_TYPE of HOSTNAME. + * @paarm port - port field. + */ + public Socks5Message(int cmd, String hostName, int port) { + super(cmd, null, port); + this.host = hostName; + this.version = SOCKS_VERSION; + + log.debug("Doing ATYP_DOMAINNAME"); + + addrType = SOCKS_ATYP_DOMAINNAME; + final byte addr[] = hostName.getBytes(); + + data = new byte[7 + addr.length]; + data[0] = (byte) SOCKS_VERSION; // Version + data[1] = (byte) command; // Command + data[2] = (byte) 0; // Reserved byte + data[3] = (byte) SOCKS_ATYP_DOMAINNAME; // Address type + data[4] = (byte) addr.length; // Length of the address + + // Put Address + System.arraycopy(addr, 0, data, 5, addr.length); + // Put port + data[data.length - 2] = (byte) (port >> 8); + data[data.length - 1] = (byte) (port); + } + + /** + * Initialises Message from the stream. Reads server response from given + * stream. + * + * @param in + * Input stream to read response from. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0), or if any + * error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public Socks5Message(InputStream in) throws SocksException, IOException { + this(in, true); + } + + /** + * Initialises Message from the stream. Reads server response or client + * request from given stream. + * + * @param in + * Input stream to read response from. + * @param clinetMode + * If true read server response, else read client request. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0) and reading + * in client mode, or if any error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public Socks5Message(InputStream in, boolean clientMode) + throws SocksException, IOException { + + read(in, clientMode); + } + + /** + * Initialises Message from the stream. Reads server response from given + * stream. + * + * @param in + * Input stream to read response from. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0), or if any + * error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public void read(InputStream in) throws SocksException, IOException { + read(in, true); + } + + /** + * Initialises Message from the stream. Reads server response or client + * request from given stream. + * + * @param in + * Input stream to read response from. + * @param clinetMode + * If true read server response, else read client request. + * @throws SocksException + * If server response code is not SOCKS_SUCCESS(0) and reading + * in client mode, or if any error with protocol occurs. + * @throws IOException + * If any error happens with I/O. + */ + public void read(InputStream in, boolean clientMode) throws SocksException, + IOException { + + data = null; + ip = null; + + final DataInputStream di = new DataInputStream(in); + + version = di.readUnsignedByte(); + command = di.readUnsignedByte(); + + if (clientMode && (command != 0)) { + throw new SocksException(command); + } + + di.readUnsignedByte(); + addrType = di.readUnsignedByte(); + + byte addr[]; + + switch (addrType) { + case SOCKS_ATYP_IPV4: + addr = new byte[4]; + di.readFully(addr); + host = bytes2IPV4(addr, 0); + break; + case SOCKS_ATYP_IPV6: + addr = new byte[SOCKS_IPV6_LENGTH];// I believe it is 16 bytes,huge! + di.readFully(addr); + host = bytes2IPV6(addr, 0); + break; + case SOCKS_ATYP_DOMAINNAME: + log.debug("Reading ATYP_DOMAINNAME"); + addr = new byte[di.readUnsignedByte()];// Next byte shows the length + di.readFully(addr); + host = new String(addr); + break; + default: + throw (new SocksException(SocksProxyBase.SOCKS_JUST_ERROR)); + } + + port = di.readUnsignedShort(); + + if ((addrType != SOCKS_ATYP_DOMAINNAME) && doResolveIP) { + try { + ip = InetAddress.getByName(host); + } catch (final UnknownHostException uh_ex) { + } + } + } + + /** + * Writes the message to the stream. + * + * @param out + * Output stream to which message should be written. + */ + public void write(OutputStream out) throws SocksException, IOException { + if (data == null) { + Socks5Message msg; + + if (addrType == SOCKS_ATYP_DOMAINNAME) { + msg = new Socks5Message(command, host, port); + } else { + if (ip == null) { + try { + ip = InetAddress.getByName(host); + } catch (final UnknownHostException uh_ex) { + throw new SocksException( + SocksProxyBase.SOCKS_JUST_ERROR); + } + } + msg = new Socks5Message(command, ip, port); + } + data = msg.data; + } + out.write(data); + } + + /** + * Returns IP field of the message as IP, if the message was created with + * ATYP of HOSTNAME, it will attempt to resolve the hostname, which might + * fail. + * + * @throws UnknownHostException + * if host can't be resolved. + */ + public InetAddress getInetAddress() throws UnknownHostException { + if (ip != null) { + return ip; + } + + return (ip = InetAddress.getByName(host)); + } + + /** + * Returns string representation of the message. + */ + public String toString() { + // FIXME: Single line version, please. + final String s = "Socks5Message:" + "\n" + "VN " + version + "\n" + + "CMD " + command + "\n" + "ATYP " + addrType + "\n" + + "ADDR " + host + "\n" + "PORT " + port + "\n"; + return s; + } + + /** + *Wether to resolve hostIP returned from SOCKS server that is wether to + * create InetAddress object from the hostName string + */ + static public boolean resolveIP() { + return doResolveIP; + } + + /** + *Wether to resolve hostIP returned from SOCKS server that is wether to + * create InetAddress object from the hostName string + * + * @param doResolve + * Wether to resolve hostIP from SOCKS server. + *@return Previous value. + */ + static public boolean resolveIP(boolean doResolve) { + final boolean old = doResolveIP; + doResolveIP = doResolve; + return old; + } + + /* + * private static final void debug(String s){ if(DEBUG) System.out.print(s); + * } private static final boolean DEBUG = false; + */ + + // SOCKS5 constants + public static final int SOCKS_VERSION = 5; + + public static final int SOCKS_ATYP_IPV4 = 0x1; // Where is 2?? + public static final int SOCKS_ATYP_DOMAINNAME = 0x3; // !!!!rfc1928 + public static final int SOCKS_ATYP_IPV6 = 0x4; + + public static final int SOCKS_IPV6_LENGTH = 16; + + static boolean doResolveIP = true; + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/Socks5Proxy.java b/src/com/runjva/sourceforge/jsocks/protocol/Socks5Proxy.java new file mode 100644 index 00000000..7400aa6e --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/Socks5Proxy.java @@ -0,0 +1,295 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * SOCKS5 Proxy. + */ + +public class Socks5Proxy extends SocksProxyBase implements Cloneable { + + // Data members + private Hashtable authMethods = new Hashtable(); + private int selectedMethod; + + boolean resolveAddrLocally = true; + UDPEncapsulation udp_encapsulation = null; + + // Public Constructors + // ==================== + + /** + * Creates SOCKS5 proxy. + * + * @param p + * Proxy to use to connect to this proxy, allows proxy chaining. + * @param proxyHost + * Host on which a Proxy server runs. + * @param proxyPort + * Port on which a Proxy server listens for connections. + * @throws UnknownHostException + * If proxyHost can't be resolved. + */ + public Socks5Proxy(SocksProxyBase p, String proxyHost, int proxyPort) + throws UnknownHostException { + + super(p, proxyHost, proxyPort); + version = 5; + setAuthenticationMethod(0, new AuthenticationNone()); + } + + /** + * Creates SOCKS5 proxy. + * + * @param proxyHost + * Host on which a Proxy server runs. + * @param proxyPort + * Port on which a Proxy server listens for connections. + * @throws UnknownHostException + * If proxyHost can't be resolved. + */ + public Socks5Proxy(String proxyHost, int proxyPort) + throws UnknownHostException { + this(null, proxyHost, proxyPort); + } + + /** + * Creates SOCKS5 proxy. + * + * @param p + * Proxy to use to connect to this proxy, allows proxy chaining. + * @param proxyIP + * Host on which a Proxy server runs. + * @param proxyPort + * Port on which a Proxy server listens for connections. + */ + public Socks5Proxy(SocksProxyBase p, InetAddress proxyIP, int proxyPort) { + super(p, proxyIP, proxyPort); + version = 5; + setAuthenticationMethod(0, new AuthenticationNone()); + } + + /** + * Creates SOCKS5 proxy. + * + * @param proxyIP + * Host on which a Proxy server runs. + * @param proxyPort + * Port on which a Proxy server listens for connections. + */ + public Socks5Proxy(InetAddress proxyIP, int proxyPort) { + this(null, proxyIP, proxyPort); + } + + // Public instance methods + // ======================== + + /** + * Wether to resolve address locally or to let proxy do so. + *

+ * SOCKS5 protocol allows to send host names rather then IPs in the + * requests, this option controls wether the hostnames should be send to the + * proxy server as names, or should they be resolved locally. + * + * @param doResolve + * Wether to perform resolution locally. + * @return Previous settings. + */ + public boolean resolveAddrLocally(boolean doResolve) { + final boolean old = resolveAddrLocally; + resolveAddrLocally = doResolve; + return old; + } + + /** + * Get current setting on how the addresses should be handled. + * + * @return Current setting for address resolution. + * @see Socks5Proxy#resolveAddrLocally(boolean doResolve) + */ + public boolean resolveAddrLocally() { + return resolveAddrLocally; + } + + /** + * Adds another authentication method. + * + * @param methodId + * Authentication method id, see rfc1928 + * @param method + * Implementation of Authentication + * @see Authentication + */ + public boolean setAuthenticationMethod(int methodId, Authentication method) { + if ((methodId < 0) || (methodId > 255)) { + return false; + } + if (method == null) { + // Want to remove a particular method + return (authMethods.remove(new Integer(methodId)) != null); + } else {// Add the method, or rewrite old one + authMethods.put(new Integer(methodId), method); + } + return true; + } + + /** + * Get authentication method, which corresponds to given method id + * + * @param methodId + * Authentication method id. + * @return Implementation for given method or null, if one was not set. + */ + public Authentication getAuthenticationMethod(int methodId) { + final Object method = authMethods.get(new Integer(methodId)); + if (method == null) { + return null; + } + return (Authentication) method; + } + + /** + * Creates a clone of this Proxy. clone() returns an + */ + @SuppressWarnings("unchecked") + public Object clone() { + final Socks5Proxy newProxy = new Socks5Proxy(proxyIP, proxyPort); + + final Object o = this.authMethods.clone(); + newProxy.authMethods = (Hashtable) o; + + newProxy.directHosts = (InetRange) directHosts.clone(); + newProxy.resolveAddrLocally = resolveAddrLocally; + newProxy.chainProxy = chainProxy; + return newProxy; + } + + // Public Static(Class) Methods + // ============================== + + // Protected Methods + // ================= + + protected SocksProxyBase copy() { + final Socks5Proxy copy = new Socks5Proxy(proxyIP, proxyPort); + + copy.authMethods = this.authMethods; // same Hash, no copy + copy.directHosts = this.directHosts; + copy.chainProxy = this.chainProxy; + copy.resolveAddrLocally = this.resolveAddrLocally; + return copy; + } + + /** + * + * + */ + protected void startSession() throws SocksException { + super.startSession(); + Authentication auth; + final Socket ps = proxySocket; // The name is too long + + try { + + final byte nMethods = (byte) authMethods.size(); // Number of + // methods + + final byte[] buf = new byte[2 + nMethods]; // 2 is for VER,NMETHODS + buf[0] = (byte) version; + buf[1] = nMethods; // Number of methods + int i = 2; + + final Enumeration ids = authMethods.keys(); + while (ids.hasMoreElements()) { + buf[i++] = (byte) ids.nextElement().intValue(); + } + + out.write(buf); + out.flush(); + + final int versionNumber = in.read(); + selectedMethod = in.read(); + + if ((versionNumber < 0) || (selectedMethod < 0)) { + // EOF condition was reached + endSession(); + final String s = "Connection to proxy lost."; + throw new SocksException(SOCKS_PROXY_IO_ERROR, s); + } + + if (versionNumber < version) { + // What should we do?? + } + + if (selectedMethod == 0xFF) { // No method selected + ps.close(); + throw (new SocksException(SOCKS_AUTH_NOT_SUPPORTED)); + } + + auth = getAuthenticationMethod(selectedMethod); + if (auth == null) { + // This shouldn't happen, unless method was removed by other + // thread, or the server stuffed up + final String s = "Specified Authentication not found!"; + throw new SocksException(SOCKS_JUST_ERROR, s); + } + + final Object[] in_out; + in_out = auth.doSocksAuthentication(selectedMethod, ps); + + if (in_out == null) { + // Authentication failed by some reason + throw (new SocksException(SOCKS_AUTH_FAILURE)); + } + + /* + * Most authentication methods are expected to return simply the + * input/output streams associated with the socket. However if the + * auth. method requires some kind of encryption/decryption being + * done on the connection it should provide classes to handle I/O. + */ + + in = (InputStream) in_out[0]; + out = (OutputStream) in_out[1]; + if (in_out.length > 2) { + udp_encapsulation = (UDPEncapsulation) in_out[2]; + } + + } catch (final SocksException s_ex) { + throw s_ex; + } catch (final UnknownHostException uh_ex) { + throw new SocksException(SOCKS_PROXY_NO_CONNECT, uh_ex); + } catch (final SocketException so_ex) { + throw new SocksException(SOCKS_PROXY_NO_CONNECT, so_ex); + } catch (final IOException io_ex) { + throw new SocksException(SOCKS_PROXY_IO_ERROR, io_ex); + } + } + + protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) { + return new Socks5Message(cmd, ip, port); + } + + protected ProxyMessage formMessage(int cmd, String host, int port) + throws UnknownHostException { + if (resolveAddrLocally) { + return formMessage(cmd, InetAddress.getByName(host), port); + } else { + return new Socks5Message(cmd, host, port); + } + } + + protected ProxyMessage formMessage(InputStream in) throws SocksException, + IOException { + return new Socks5Message(in); + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/SocksException.java b/src/com/runjva/sourceforge/jsocks/protocol/SocksException.java new file mode 100644 index 00000000..a6515974 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/SocksException.java @@ -0,0 +1,111 @@ +package com.runjva.sourceforge.jsocks.protocol; + +/** + * Exception thrown by various socks classes to indicate errors with protocol or + * unsuccessfull server responses. + */ +public class SocksException extends java.io.IOException { + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Construct a SocksException with given errorcode. + *

+ * Tries to look up message which corresponds to this error code. + * + * @param errCode + * Error code for this exception. + */ + public SocksException(int errCode) { + this.errCode = errCode; + lookupErrorString(errCode); + } + + private void lookupErrorString(int errCode) { + if ((errCode >> 16) == 0) { + if (errCode <= serverReplyMessage.length) { + errString = serverReplyMessage[errCode]; + } else { + errString = UNASSIGNED_ERROR_MESSAGE; + } + } else { + // Local error + errCode = (errCode >> 16) - 1; + if (errCode <= localErrorMessage.length) { + errString = localErrorMessage[errCode]; + } else { + errString = UNASSIGNED_ERROR_MESSAGE; + } + } + } + + /** + * Construct a SocksException with given error code, and a Throwable cause + * + * @param errCode + * @param t + * Nested exception for debugging purposes. + */ + public SocksException(int errCode, Throwable t) { + super(t); // Java 1.6+ + this.errCode = errCode; + lookupErrorString(errCode); + } + + /** + * Constructs a SocksException with given error code and message. + * + * @param errCode + * Error code. + * @param errString + * Error Message. + */ + public SocksException(int errCode, String errString) { + this.errCode = errCode; + this.errString = errString; + } + + public SocksException(int errCode, String string, Throwable t) { + super(string, t); // Java 1.6+ + this.errCode = errCode; + this.errString = string; + } + + /** + * Get the error code associated with this exception. + * + * @return Error code associated with this exception. + */ + public int getErrorCode() { + return errCode; + } + + /** + * Get human readable representation of this exception. + * + * @return String represntation of this exception. + */ + public String toString() { + return errString; + } + + static final String UNASSIGNED_ERROR_MESSAGE = "Unknown error message"; + + static final String serverReplyMessage[] = { "Succeeded", + "General SOCKS server failure", + "Connection not allowed by ruleset", "Network unreachable", + "Host unreachable", "Connection refused", "TTL expired", + "Command not supported", "Address type not supported" }; + + static final String localErrorMessage[] = { "SOCKS server not specified", + "Unable to contact SOCKS server", "IO error", + "None of Authentication methods are supported", + "Authentication failed", "General SOCKS fault" }; + + String errString; + int errCode; + +}// End of SocksException class + diff --git a/src/com/runjva/sourceforge/jsocks/protocol/SocksProxyBase.java b/src/com/runjva/sourceforge/jsocks/protocol/SocksProxyBase.java new file mode 100644 index 00000000..c4ce565b --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/SocksProxyBase.java @@ -0,0 +1,543 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy. Defines + * methods for specifying default proxy, to be used by all classes of this + * package. + */ + +public abstract class SocksProxyBase { + + // Data members + protected InetRange directHosts = new InetRange(); + + protected InetAddress proxyIP = null; + protected String proxyHost = null; + protected int proxyPort; + protected Socket proxySocket = null; + + protected InputStream in; + protected OutputStream out; + + protected int version; + + protected SocksProxyBase chainProxy = null; + + // Protected static/class variables + protected static SocksProxyBase defaultProxy = null; + + // Constructors + // ==================== + SocksProxyBase(SocksProxyBase chainProxy, String proxyHost, int proxyPort) + throws UnknownHostException { + this.chainProxy = chainProxy; + this.proxyHost = proxyHost; + + if (chainProxy == null) { + this.proxyIP = InetAddress.getByName(proxyHost); + } + + this.proxyPort = proxyPort; + } + + SocksProxyBase(String proxyHost, int proxyPort) throws UnknownHostException { + this(null, proxyHost, proxyPort); + } + + SocksProxyBase(SocksProxyBase chainProxy, InetAddress proxyIP, int proxyPort) { + this.chainProxy = chainProxy; + this.proxyIP = proxyIP; + this.proxyPort = proxyPort; + } + + SocksProxyBase(InetAddress proxyIP, int proxyPort) { + this(null, proxyIP, proxyPort); + } + + SocksProxyBase(SocksProxyBase p) { + this.proxyIP = p.proxyIP; + this.proxyPort = p.proxyPort; + this.version = p.version; + this.directHosts = p.directHosts; + } + + // Public instance methods + // ======================== + + /** + * Get the port on which proxy server is running. + * + * @return Proxy port. + */ + public int getPort() { + return proxyPort; + } + + /** + * Get the ip address of the proxy server host. + * + * @return Proxy InetAddress. + */ + public InetAddress getInetAddress() { + return proxyIP; + } + + /** + * Adds given ip to the list of direct addresses. This machine will be + * accessed without using proxy. + */ + public void addDirect(InetAddress ip) { + directHosts.add(ip); + } + + /** + * Adds host to the list of direct addresses. This machine will be accessed + * without using proxy. + */ + public boolean addDirect(String host) { + return directHosts.add(host); + } + + /** + * Adds given range of addresses to the lsit of direct addresses, machines + * within this range will be accessed without using proxy. + */ + public void addDirect(InetAddress from, InetAddress to) { + directHosts.add(from, to); + } + + /** + * Sets given InetRange as the list of direct address, previous list will be + * discarded, any changes done previously with addDirect(Inetaddress) will + * be lost. The machines in this range will be accessed without using proxy. + * + * @param ir + * InetRange which should be used to look up direct addresses. + * @see InetRange + */ + public void setDirect(InetRange ir) { + directHosts = ir; + } + + /** + * Get the list of direct hosts. + * + * @return Current range of direct address as InetRange object. + * @see InetRange + */ + public InetRange getDirect() { + return directHosts; + } + + /** + * Check wether the given host is on the list of direct address. + * + * @param host + * Host name to check. + * @return true if the given host is specified as the direct addresses. + */ + public boolean isDirect(String host) { + return directHosts.contains(host); + } + + /** + * Check wether the given host is on the list of direct addresses. + * + * @param host + * Host address to check. + * @return true if the given host is specified as the direct address. + */ + public boolean isDirect(InetAddress host) { + return directHosts.contains(host); + } + + /** + * Set the proxy which should be used to connect to given proxy. + * + * @param chainProxy + * Proxy to use to connect to this proxy. + */ + public void setChainProxy(SocksProxyBase chainProxy) { + this.chainProxy = chainProxy; + } + + /** + * Get proxy which is used to connect to this proxy. + * + * @return Proxy which is used to connect to this proxy, or null if proxy is + * to be contacted directly. + */ + public SocksProxyBase getChainProxy() { + return chainProxy; + } + + /** + * Get string representation of this proxy. + * + * @returns string in the form:proxyHost:proxyPort \t Version versionNumber + */ + public String toString() { + return ("" + proxyIP.getHostName() + ":" + proxyPort + "\tVersion " + version); + } + + // Public Static(Class) Methods + // ============================== + + /** + * Sets SOCKS4 proxy as default. + * + * @param hostName + * Host name on which SOCKS4 server is running. + * @param port + * Port on which SOCKS4 server is running. + * @param user + * Username to use for communications with proxy. + */ + public static void setDefaultProxy(String hostName, int port, String user) + throws UnknownHostException { + defaultProxy = new Socks4Proxy(hostName, port, user); + } + + /** + * Sets SOCKS4 proxy as default. + * + * @param ipAddress + * Host address on which SOCKS4 server is running. + * @param port + * Port on which SOCKS4 server is running. + * @param user + * Username to use for communications with proxy. + */ + public static void setDefaultProxy(InetAddress ipAddress, int port, + String user) { + defaultProxy = new Socks4Proxy(ipAddress, port, user); + } + + /** + * Sets SOCKS5 proxy as default. Default proxy only supports + * no-authentication. + * + * @param hostName + * Host name on which SOCKS5 server is running. + * @param port + * Port on which SOCKS5 server is running. + */ + public static void setDefaultProxy(String hostName, int port) + throws UnknownHostException { + defaultProxy = new Socks5Proxy(hostName, port); + } + + /** + * Sets SOCKS5 proxy as default. Default proxy only supports + * no-authentication. + * + * @param ipAddress + * Host address on which SOCKS5 server is running. + * @param port + * Port on which SOCKS5 server is running. + */ + public static void setDefaultProxy(InetAddress ipAddress, int port) { + defaultProxy = new Socks5Proxy(ipAddress, port); + } + + /** + * Sets default proxy. + * + * @param p + * Proxy to use as default proxy. + */ + public static void setDefaultProxy(SocksProxyBase p) { + defaultProxy = p; + } + + /** + * Get current default proxy. + * + * @return Current default proxy, or null if none is set. + */ + public static SocksProxyBase getDefaultProxy() { + return defaultProxy; + } + + /** + * Parses strings in the form: host[:port:user:password], and creates proxy + * from information obtained from parsing. + *

+ * Defaults: port = 1080.
+ * If user specified but not password, creates Socks4Proxy, if user not + * specified creates Socks5Proxy, if both user and password are speciefied + * creates Socks5Proxy with user/password authentication. + * + * @param proxy_entry + * String in the form host[:port:user:password] + * @return Proxy created from the string, null if entry was somehow + * invalid(host unknown for example, or empty string) + */ + public static SocksProxyBase parseProxy(String proxy_entry) { + + String proxy_host; + int proxy_port = 1080; + String proxy_user = null; + String proxy_password = null; + SocksProxyBase proxy; + + final java.util.StringTokenizer st = new java.util.StringTokenizer( + proxy_entry, ":"); + if (st.countTokens() < 1) { + return null; + } + + proxy_host = st.nextToken(); + if (st.hasMoreTokens()) { + try { + proxy_port = Integer.parseInt(st.nextToken().trim()); + } catch (final NumberFormatException nfe) { + } + } + + if (st.hasMoreTokens()) { + proxy_user = st.nextToken(); + } + + if (st.hasMoreTokens()) { + proxy_password = st.nextToken(); + } + + try { + if (proxy_user == null) { + proxy = new Socks5Proxy(proxy_host, proxy_port); + } else if (proxy_password == null) { + proxy = new Socks4Proxy(proxy_host, proxy_port, proxy_user); + } else { + proxy = new Socks5Proxy(proxy_host, proxy_port); + final UserPasswordAuthentication upa = new UserPasswordAuthentication( + proxy_user, proxy_password); + + ((Socks5Proxy) proxy).setAuthenticationMethod( + UserPasswordAuthentication.METHOD_ID, upa); + } + } catch (final UnknownHostException uhe) { + return null; + } + + return proxy; + } + + // Protected Methods + // ================= + + protected void startSession() throws SocksException { + try { + if (chainProxy == null) { + proxySocket = new Socket(proxyIP, proxyPort); + } else if (proxyIP != null) { + proxySocket = new SocksSocket(chainProxy, proxyIP, proxyPort); + } else { + proxySocket = new SocksSocket(chainProxy, proxyHost, proxyPort); + } + + in = proxySocket.getInputStream(); + out = proxySocket.getOutputStream(); + } catch (final SocksException se) { + throw se; + } catch (final IOException io_ex) { + throw new SocksException(SOCKS_PROXY_IO_ERROR, "" + io_ex); + } + } + + /** + * Create a copy of this proxy for use by individual threads. + * + * @return proxy + */ + protected abstract SocksProxyBase copy(); + + protected abstract ProxyMessage formMessage(int cmd, InetAddress ip, + int port); + + protected abstract ProxyMessage formMessage(int cmd, String host, int port) + throws UnknownHostException; + + protected abstract ProxyMessage formMessage(InputStream in) + throws SocksException, IOException; + + protected ProxyMessage connect(InetAddress ip, int port) + throws SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, ip, + port); + return exchange(request); + } catch (final SocksException se) { + endSession(); + throw se; + } + } + + protected ProxyMessage connect(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_CONNECT, host, + port); + return exchange(request); + } catch (final SocksException se) { + endSession(); + throw se; + } + } + + protected ProxyMessage bind(InetAddress ip, int port) throws SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_BIND, ip, port); + return exchange(request); + } catch (final SocksException se) { + endSession(); + throw se; + } + } + + protected ProxyMessage bind(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_BIND, host, port); + return exchange(request); + } catch (final SocksException se) { + endSession(); + throw se; + } + } + + protected ProxyMessage accept() throws IOException, SocksException { + ProxyMessage msg; + try { + msg = formMessage(in); + } catch (final InterruptedIOException iioe) { + throw iioe; + } catch (final IOException io_ex) { + endSession(); + throw new SocksException(SOCKS_PROXY_IO_ERROR, + "While Trying accept:" + io_ex); + } + return msg; + } + + protected ProxyMessage udpAssociate(InetAddress ip, int port) + throws SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, + ip, port); + if (request != null) { + return exchange(request); + } + } catch (final SocksException se) { + endSession(); + throw se; + } + // Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + + protected ProxyMessage udpAssociate(String host, int port) + throws UnknownHostException, SocksException { + try { + startSession(); + final ProxyMessage request = formMessage(SOCKS_CMD_UDP_ASSOCIATE, + host, port); + if (request != null) { + return exchange(request); + } + } catch (final SocksException se) { + endSession(); + throw se; + } + // Only get here if request was null + endSession(); + throw new SocksException(SOCKS_METHOD_NOTSUPPORTED, + "This version of proxy does not support UDP associate, use version 5"); + } + + protected void endSession() { + try { + if (proxySocket != null) { + proxySocket.close(); + } + proxySocket = null; + } catch (final IOException io_ex) { + } + } + + /** + *Sends the request to SOCKS server + */ + protected void sendMsg(ProxyMessage msg) throws SocksException, IOException { + msg.write(out); + } + + /** + * Reads the reply from the SOCKS server + */ + protected ProxyMessage readMsg() throws SocksException, IOException { + return formMessage(in); + } + + /** + *Sends the request reads reply and returns it throws exception if + * something wrong with IO or the reply code is not zero + */ + protected ProxyMessage exchange(ProxyMessage request) throws SocksException { + ProxyMessage reply; + try { + request.write(out); + reply = formMessage(in); + } catch (final SocksException s_ex) { + throw s_ex; + } catch (final IOException ioe) { + throw (new SocksException(SOCKS_PROXY_IO_ERROR, "" + ioe)); + } + return reply; + } + + // Private methods + // =============== + + // Constants + + public static final int SOCKS_SUCCESS = 0; + public static final int SOCKS_FAILURE = 1; + public static final int SOCKS_BADCONNECT = 2; + public static final int SOCKS_BADNETWORK = 3; + public static final int SOCKS_HOST_UNREACHABLE = 4; + public static final int SOCKS_CONNECTION_REFUSED = 5; + public static final int SOCKS_TTL_EXPIRE = 6; + public static final int SOCKS_CMD_NOT_SUPPORTED = 7; + public static final int SOCKS_ADDR_NOT_SUPPORTED = 8; + + public static final int SOCKS_NO_PROXY = 1 << 16; + public static final int SOCKS_PROXY_NO_CONNECT = 2 << 16; + public static final int SOCKS_PROXY_IO_ERROR = 3 << 16; + public static final int SOCKS_AUTH_NOT_SUPPORTED = 4 << 16; + public static final int SOCKS_AUTH_FAILURE = 5 << 16; + public static final int SOCKS_JUST_ERROR = 6 << 16; + + public static final int SOCKS_DIRECT_FAILED = 7 << 16; + public static final int SOCKS_METHOD_NOTSUPPORTED = 8 << 16; + + static final int SOCKS_CMD_CONNECT = 0x1; + static final int SOCKS_CMD_BIND = 0x2; + static final int SOCKS_CMD_UDP_ASSOCIATE = 0x3; + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/SocksServerSocket.java b/src/com/runjva/sourceforge/jsocks/protocol/SocksServerSocket.java new file mode 100644 index 00000000..6d812f29 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/SocksServerSocket.java @@ -0,0 +1,238 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; + +/** + * SocksServerSocket allows to accept connections from one particular host + * through the SOCKS4 or SOCKS5 proxy. + */ +public class SocksServerSocket extends ServerSocket { + // Data members + protected SocksProxyBase proxy; + protected String localHost; + protected InetAddress localIP; + protected int localPort; + + boolean doing_direct = false; + InetAddress remoteAddr; + + /** + * Creates ServerSocket capable of accepting one connection through the + * firewall, uses default Proxy. + * + * @param host + * Host from which the connection should be recieved. + *@param port + * Port number of the primary connection. + */ + public SocksServerSocket(String host, int port) throws SocksException, + UnknownHostException, IOException { + this(SocksProxyBase.defaultProxy, host, port); + } + + /** + *Creates ServerSocket capable of accepting one connection through the + * firewall, uses given proxy. + * + * @param p + * Proxy object to use. + *@param host + * Host from which the connection should be recieved. + *@param port + * Port number of the primary connection. + */ + public SocksServerSocket(SocksProxyBase p, String host, int port) + throws SocksException, UnknownHostException, IOException { + + super(0); + if (p == null) { + throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY); + } + // proxy=p; + proxy = p.copy(); + if (proxy.isDirect(host)) { + remoteAddr = InetAddress.getByName(host); + proxy = null; + doDirect(); + } else { + processReply(proxy.bind(host, port)); + } + } + + /** + * Creates ServerSocket capable of accepting one connection through the + * firewall, uses default Proxy. + * + * @param ip + * Host from which the connection should be recieved. + *@param port + * Port number of the primary connection. + */ + public SocksServerSocket(InetAddress ip, int port) throws SocksException, + IOException { + this(SocksProxyBase.defaultProxy, ip, port); + } + + /** + *Creates ServerSocket capable of accepting one connection through the + * firewall, uses given proxy. + * + * @param p + * Proxy object to use. + *@param ip + * Host from which the connection should be recieved. + *@param port + * Port number of the primary connection. + */ + public SocksServerSocket(SocksProxyBase p, InetAddress ip, int port) + throws SocksException, IOException { + super(0); + + if (p == null) { + throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY); + } + this.proxy = p.copy(); + + if (proxy.isDirect(ip)) { + remoteAddr = ip; + doDirect(); + } else { + processReply(proxy.bind(ip, port)); + } + } + + /** + * Accepts the incoming connection. + */ + public Socket accept() throws IOException { + Socket s; + + if (!doing_direct) { + if (proxy == null) { + return null; + } + + final ProxyMessage msg = proxy.accept(); + s = msg.ip == null ? new SocksSocket(msg.host, msg.port, proxy) + : new SocksSocket(msg.ip, msg.port, proxy); + // Set timeout back to 0 + proxy.proxySocket.setSoTimeout(0); + if (ProxyServer.vpnService != null) + ProxyServer.vpnService.protect(proxy.proxySocket); + + } else { // Direct Connection + + // Mimic the proxy behaviour, + // only accept connections from the speciefed host. + while (true) { + s = super.accept(); + if (s.getInetAddress().equals(remoteAddr)) { + // got the connection from the right host + // Close listenning socket. + break; + } else { + s.close(); // Drop all connections from other hosts + } + } + + } + proxy = null; + // Return accepted socket + return s; + } + + /** + * Closes the connection to proxy if socket have not been accepted, if the + * direct connection is used, closes direct ServerSocket. If the client + * socket have been allready accepted, does nothing. + */ + public void close() throws IOException { + super.close(); + if (proxy != null) { + proxy.endSession(); + } + proxy = null; + } + + /** + * Get the name of the host proxy is using to listen for incoming + * connection. + *

+ * Usefull when address is returned by proxy as the hostname. + * + * @return the hostname of the address proxy is using to listen for incoming + * connection. + */ + public String getHost() { + return localHost; + } + + /** + * Get address assigned by proxy to listen for incomming connections, or the + * local machine address if doing direct connection. + */ + public InetAddress getInetAddress() { + if (localIP == null) { + try { + localIP = InetAddress.getByName(localHost); + } catch (final UnknownHostException e) { + return null; + } + } + return localIP; + } + + /** + * Get port assigned by proxy to listen for incoming connections, or the + * port chosen by local system, if accepting directly. + */ + public int getLocalPort() { + return localPort; + } + + /** + * Set Timeout. + * + * @param timeout + * Amount of time in milliseconds, accept should wait for + * incoming connection before failing with exception. Zero + * timeout implies infinity. + */ + public void setSoTimeout(int timeout) throws SocketException { + super.setSoTimeout(timeout); + if (!doing_direct) { + proxy.proxySocket.setSoTimeout(timeout); + } + } + + // Private Methods + // //////////////// + + private void processReply(ProxyMessage reply) throws SocksException { + localPort = reply.port; + /* + * If the server have assigned same host as it was contacted on it might + * return an address of all zeros + */ + if (reply.host.equals("0.0.0.0")) { + localIP = proxy.proxyIP; + localHost = localIP.getHostName(); + } else { + localHost = reply.host; + localIP = reply.ip; + } + } + + private void doDirect() { + doing_direct = true; + localPort = super.getLocalPort(); + localIP = super.getInetAddress(); + localHost = localIP.getHostName(); + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/SocksSocket.java b/src/com/runjva/sourceforge/jsocks/protocol/SocksSocket.java new file mode 100644 index 00000000..fd626ec4 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/SocksSocket.java @@ -0,0 +1,389 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SocksSocket tryies to look very similar to normal Socket, while allowing + * connections through the SOCKS4 or 5 proxy. To use this class you will have to + * identify proxy you need to use, Proxy class allows you to set default proxy, + * which will be used by all Socks aware sockets. You can also create either + * Socks4Proxy or Socks5Proxy, and use them by passing to the appropriate + * constructors. + *

+ * Using Socks package can be as easy as that: + * + *

+ * <tt>
+ * 
+ *     import Socks.*;
+ *     ....
+ * 
+ *     try{
+ *        //Specify SOCKS5 proxy
+ *        Proxy.setDefaultProxy("socks-proxy",1080);
+ * 
+ *        //OR you still use SOCKS4
+ *        //Code below uses SOCKS4 proxy
+ *        //Proxy.setDefaultProxy("socks-proxy",1080,userName);
+ * 
+ *        Socket s = SocksSocket("some.host.of.mine",13);
+ *        readTimeFromSock(s);
+ *     }catch(SocksException sock_ex){
+ *        //Usually it will turn in more or less meaningfull message
+ *        System.err.println("SocksException:"+sock_ex);
+ *     }
+ * 
+ * </tt>
+ * 
+ *

+ * However if the need exist for more control, like resolving addresses + * remotely, or using some non-trivial authentication schemes, it can be done. + */ + +public class SocksSocket extends Socket { + // Data members + protected SocksProxyBase proxy; + protected String localHost, remoteHost; + protected InetAddress localIP, remoteIP; + protected int localPort, remotePort; + + private Socket directSock = null; + private Logger log = LoggerFactory.getLogger(SocksSocket.class); + + /** + * Tryies to connect to given host and port using default proxy. If no + * default proxy speciefied it throws SocksException with error code + * SOCKS_NO_PROXY. + * + * @param host + * Machine to connect to. + * @param port + * Port to which to connect. + * @see SocksSocket#SocksSocket(SocksProxyBase,String,int) + * @see Socks5Proxy#resolveAddrLocally + */ + public SocksSocket(String host, int port) throws SocksException, + UnknownHostException { + this(SocksProxyBase.defaultProxy, host, port); + } + + /** + * Connects to host port using given proxy server. + * + * @param p + * Proxy to use. + * @param host + * Machine to connect to. + * @param port + * Port to which to connect. + * @throws UnknownHostException + * If one of the following happens: + *

    + * + *
  1. Proxy settings say that address should be resolved + * locally, but this fails. + *
  2. Proxy settings say that the host should be contacted + * directly but host name can't be resolved. + *
+ * @throws SocksException + * If one of the following happens: + *
    + *
  • Proxy is is null. + *
  • Proxy settings say that the host should be contacted + * directly but this fails. + *
  • Socks Server can't be contacted. + *
  • Authentication fails. + *
  • Connection is not allowed by the SOCKS proxy. + *
  • SOCKS proxy can't establish the connection. + *
  • Any IO error occured. + *
  • Any protocol error occured. + *
+ * @throws IOexception + * if anything is wrong with I/O. + * @see Socks5Proxy#resolveAddrLocally + */ + public SocksSocket(SocksProxyBase p, String host, int port) + throws SocksException, UnknownHostException { + + if (p == null) { + throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY); + } + // proxy=p; + proxy = p.copy(); + remoteHost = host; + remotePort = port; + if (proxy.isDirect(host)) { + remoteIP = InetAddress.getByName(host); + doDirect(); + } else { + processReply(proxy.connect(host, port)); + } + } + + /** + * Tryies to connect to given ip and port using default proxy. If no default + * proxy speciefied it throws SocksException with error code SOCKS_NO_PROXY. + * + * @param ip + * Machine to connect to. + * @param port + * Port to which to connect. + * @see SocksSocket#SocksSocket(SocksProxyBase,String,int) + */ + public SocksSocket(InetAddress ip, int port) throws SocksException { + this(SocksProxyBase.defaultProxy, ip, port); + } + + /** + * Connects to given ip and port using given Proxy server. + * + * @param p + * Proxy to use. + * @param ip + * Machine to connect to. + * @param port + * Port to which to connect. + */ + public SocksSocket(SocksProxyBase p, InetAddress ip, int port) + throws SocksException { + if (p == null) { + throw new SocksException(SocksProxyBase.SOCKS_NO_PROXY); + } + this.proxy = p.copy(); + this.remoteIP = ip; + this.remotePort = port; + this.remoteHost = ip.getHostName(); + if (proxy.isDirect(remoteIP)) { + doDirect(); + } else { + processReply(proxy.connect(ip, port)); + } + } + + /** + * These 2 constructors are used by the SocksServerSocket. This socket + * simply overrides remoteHost, remotePort + */ + protected SocksSocket(String host, int port, SocksProxyBase proxy) { + this.remotePort = port; + this.proxy = proxy; + this.localIP = proxy.proxySocket.getLocalAddress(); + this.localPort = proxy.proxySocket.getLocalPort(); + this.remoteHost = host; + } + + protected SocksSocket(InetAddress ip, int port, SocksProxyBase proxy) { + remoteIP = ip; + remotePort = port; + this.proxy = proxy; + this.localIP = proxy.proxySocket.getLocalAddress(); + this.localPort = proxy.proxySocket.getLocalPort(); + remoteHost = remoteIP.getHostName(); + } + + /** + * Same as Socket + */ + public void close() throws IOException { + if (proxy != null) { + proxy.endSession(); + } + proxy = null; + } + + /** + * Same as Socket + */ + public InputStream getInputStream() { + return proxy.in; + } + + /** + * Same as Socket + */ + public OutputStream getOutputStream() { + return proxy.out; + } + + /** + * Same as Socket + */ + public int getPort() { + return remotePort; + } + + /** + * Returns remote host name, it is usefull in cases when addresses are + * resolved by proxy, and we can't create InetAddress object. + * + * @return The name of the host this socket is connected to. + */ + public String getHost() { + return remoteHost; + } + + /** + * Get remote host as InetAddress object, might return null if addresses are + * resolved by proxy, and it is not possible to resolve it locally + * + * @return Ip address of the host this socket is connected to, or null if + * address was returned by the proxy as DOMAINNAME and can't be + * resolved locally. + */ + public InetAddress getInetAddress() { + if (remoteIP == null) { + try { + remoteIP = InetAddress.getByName(remoteHost); + } catch (final UnknownHostException e) { + return null; + } + } + return remoteIP; + } + + /** + * Get the port assigned by the proxy for the socket, not the port on locall + * machine as in Socket. + * + * @return Port of the socket used on the proxy server. + */ + public int getLocalPort() { + return localPort; + } + + /** + * Get address assigned by proxy to make a remote connection, it might be + * different from the host specified for the proxy. Can return null if socks + * server returned this address as hostname and it can't be resolved + * locally, use getLocalHost() then. + * + * @return Address proxy is using to make a connection. + */ + public InetAddress getLocalAddress() { + if (localIP == null) { + try { + localIP = InetAddress.getByName(localHost); + } catch (final UnknownHostException e) { + return null; + } + } + return localIP; + } + + /** + * Get name of the host, proxy has assigned to make a remote connection for + * this socket. This method is usefull when proxy have returned address as + * hostname, and we can't resolve it on this machine. + * + * @return The name of the host proxy is using to make a connection. + */ + public String getLocalHost() { + return localHost; + } + + /** + * Same as socket. + */ + public void setSoLinger(boolean on, int val) throws SocketException { + proxy.proxySocket.setSoLinger(on, val); + } + + /** + * Same as socket. + */ + public int getSoLinger(int timeout) throws SocketException { + return proxy.proxySocket.getSoLinger(); + } + + /** + * Same as socket. + */ + public void setSoTimeout(int timeout) throws SocketException { + proxy.proxySocket.setSoTimeout(timeout); + } + + /** + * Same as socket. + */ + public int getSoTimeout(int timeout) throws SocketException { + return proxy.proxySocket.getSoTimeout(); + } + + /** + * Same as socket. + */ + public void setTcpNoDelay(boolean on) throws SocketException { + proxy.proxySocket.setTcpNoDelay(on); + } + + /** + * Same as socket. + */ + public boolean getTcpNoDelay() throws SocketException { + return proxy.proxySocket.getTcpNoDelay(); + } + + /** + * Get string representation of the socket. + */ + public String toString() { + if (directSock != null) { + return "Direct connection:" + directSock; + } + StringBuffer sb = new StringBuffer(); + sb.append("Proxy:"); + sb.append(proxy); + sb.append(";"); + sb.append("addr:"); + sb.append(remoteHost); + sb.append(",port:"); + sb.append(remotePort); + sb.append(",localport:"); + sb.append(localPort); + return sb.toString(); + + } + + // Private Methods + // //////////////// + + private void processReply(ProxyMessage reply) throws SocksException { + localPort = reply.port; + /* + * If the server have assigned same host as it was contacted on it might + * return an address of all zeros + */ + if (reply.host.equals("0.0.0.0")) { + localIP = proxy.proxyIP; + localHost = localIP.getHostName(); + } else { + localHost = reply.host; + localIP = reply.ip; + } + } + + private void doDirect() throws SocksException { + try { + log.debug("IP: {}_{}", remoteIP, remotePort); + directSock = new Socket(remoteIP, remotePort); + proxy.out = directSock.getOutputStream(); + proxy.in = directSock.getInputStream(); + proxy.proxySocket = directSock; + localIP = directSock.getLocalAddress(); + localPort = directSock.getLocalPort(); + } catch (final IOException io_ex) { + final int errCode = SocksProxyBase.SOCKS_DIRECT_FAILED; + throw new SocksException(errCode, "Direct connect failed:", io_ex); + } + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/UDPEncapsulation.java b/src/com/runjva/sourceforge/jsocks/protocol/UDPEncapsulation.java new file mode 100644 index 00000000..913bb31c --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/UDPEncapsulation.java @@ -0,0 +1,33 @@ +package com.runjva.sourceforge.jsocks.protocol; + +/** + * This interface provides for datagram encapsulation for SOCKSv5 protocol. + *

+ * SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity + * and/or authenticity. How it should be done is aggreed during the + * authentication stage, and is authentication dependent. This interface is + * provided to allow this encapsulation. + * + * @see Authentication + */ +public interface UDPEncapsulation { + + /** + * This method should provide any authentication depended transformation on + * datagrams being send from/to the client. + * + * @param data + * Datagram data (including any SOCKS related bytes), to be + * encapsulated/decapsulated. + * @param out + * Wether the data is being send out. If true method should + * encapsulate/encrypt data, otherwise it should decapsulate/ + * decrypt data. + * @throw IOException if for some reason data can be transformed correctly. + * @return Should return byte array containing data after transformation. It + * is possible to return same array as input, if transformation only + * involves bit mangling, and no additional data is being added or + * removed. + */ + byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException; +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/UDPRelayServer.java b/src/com/runjva/sourceforge/jsocks/protocol/UDPRelayServer.java new file mode 100644 index 00000000..14599c7e --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/UDPRelayServer.java @@ -0,0 +1,231 @@ +package com.runjva.sourceforge.jsocks.protocol; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.runjva.sourceforge.jsocks.server.ServerAuthenticator; + +/** + * UDP Relay server, used by ProxyServer to perform udp forwarding. + */ +class UDPRelayServer implements Runnable { + + DatagramSocket client_sock; + DatagramSocket remote_sock; + + Socket controlConnection; + + int relayPort; + InetAddress relayIP; + + Thread pipe_thread1, pipe_thread2; + Thread master_thread; + + ServerAuthenticator auth; + + long lastReadTime; + + static Logger log = LoggerFactory.getLogger(UDPRelayServer.class); + static SocksProxyBase proxy = null; + static int datagramSize = 0xFFFF;// 64K, a bit more than max udp size + static int iddleTimeout = 180000;// 3 minutes + + /** + * Constructs UDP relay server to communicate with client on given ip and + * port. + * + * @param clientIP + * Address of the client from whom datagrams will be recieved and + * to whom they will be forwarded. + * @param clientPort + * Clients port. + * @param master_thread + * Thread which will be interrupted, when UDP relay server + * stoppes for some reason. + * @param controlConnection + * Socket which will be closed, before interrupting the master + * thread, it is introduced due to a bug in windows JVM which + * does not throw InterruptedIOException in threads which block + * in I/O operation. + */ + public UDPRelayServer(InetAddress clientIP, int clientPort, + Thread master_thread, Socket controlConnection, + ServerAuthenticator auth) throws IOException { + + this.master_thread = master_thread; + this.controlConnection = controlConnection; + this.auth = auth; + + client_sock = new Socks5DatagramSocket(true, + auth.getUdpEncapsulation(), clientIP, clientPort); + + relayPort = client_sock.getLocalPort(); + relayIP = client_sock.getLocalAddress(); + + if (relayIP.getHostAddress().equals("0.0.0.0")) { + relayIP = InetAddress.getLocalHost(); + } + + if (proxy == null) { + remote_sock = new DatagramSocket(); + } else { + remote_sock = new Socks5DatagramSocket(proxy, 0, null); + } + } + + // Public methods + // /////////////// + + /** + * Sets the timeout for UDPRelay server.
+ * Zero timeout implies infinity.
+ * Default timeout is 3 minutes. + */ + + static public void setTimeout(int timeout) { + iddleTimeout = timeout; + } + + /** + * Sets the size of the datagrams used in the UDPRelayServer.
+ * Default size is 64K, a bit more than maximum possible size of the + * datagram. + */ + static public void setDatagramSize(int size) { + datagramSize = size; + } + + /** + * Port to which client should send datagram for association. + */ + public int getRelayPort() { + return relayPort; + } + + /** + * IP address to which client should send datagrams for association. + */ + public InetAddress getRelayIP() { + return relayIP; + } + + /** + * Starts udp relay server. Spawns two threads of execution and returns. + */ + public void start() throws IOException { + remote_sock.setSoTimeout(iddleTimeout); + client_sock.setSoTimeout(iddleTimeout); + + log.info("Starting UDP relay server on {}:{}", relayIP, relayPort); + log.info("Remote socket {}:{}", remote_sock.getLocalAddress(), + remote_sock.getLocalPort()); + + pipe_thread1 = new Thread(this, "pipe1"); + pipe_thread2 = new Thread(this, "pipe2"); + + lastReadTime = System.currentTimeMillis(); + + pipe_thread1.start(); + pipe_thread2.start(); + } + + /** + * Stops Relay server. + *

+ * Does not close control connection, does not interrupt master_thread. + */ + public synchronized void stop() { + master_thread = null; + controlConnection = null; + abort(); + } + + // Runnable interface + // ////////////////// + public void run() { + try { + if (Thread.currentThread().getName().equals("pipe1")) { + pipe(remote_sock, client_sock, false); + } else { + pipe(client_sock, remote_sock, true); + } + } catch (final IOException ioe) { + } finally { + abort(); + log.info("UDP Pipe thread " + Thread.currentThread().getName() + + " stopped."); + } + + } + + // Private methods + // /////////////// + private synchronized void abort() { + if (pipe_thread1 == null) { + return; + } + + log.info("Aborting UDP Relay Server"); + + remote_sock.close(); + client_sock.close(); + + if (controlConnection != null) { + try { + controlConnection.close(); + } catch (final IOException ioe) { + } + } + + if (master_thread != null) { + master_thread.interrupt(); + } + + pipe_thread1.interrupt(); + pipe_thread2.interrupt(); + + pipe_thread1 = null; + } + + private void pipe(DatagramSocket from, DatagramSocket to, boolean out) + throws IOException { + final byte[] data = new byte[datagramSize]; + final DatagramPacket dp = new DatagramPacket(data, data.length); + + while (true) { + try { + from.receive(dp); + lastReadTime = System.currentTimeMillis(); + + if (auth.checkRequest(dp, out)) { + to.send(dp); + } + + } catch (final UnknownHostException uhe) { + log.info("Dropping datagram for unknown host"); + } catch (final InterruptedIOException iioe) { + // log("Interrupted: "+iioe); + // If we were interrupted by other thread. + if (iddleTimeout == 0) { + return; + } + + // If last datagram was received, long time ago, return. + final long timeSinceRead = System.currentTimeMillis() + - lastReadTime; + if (timeSinceRead >= iddleTimeout - 100) { + return; + } + } + dp.setLength(data.length); + } + } +} diff --git a/src/com/runjva/sourceforge/jsocks/protocol/UserPasswordAuthentication.java b/src/com/runjva/sourceforge/jsocks/protocol/UserPasswordAuthentication.java new file mode 100644 index 00000000..c161d334 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/protocol/UserPasswordAuthentication.java @@ -0,0 +1,91 @@ +package com.runjva.sourceforge.jsocks.protocol; + +/** + * SOCKS5 User Password authentication scheme. + */ +public class UserPasswordAuthentication implements Authentication { + + /** SOCKS ID for User/Password authentication method */ + public final static int METHOD_ID = 2; + + String userName, password; + byte[] request; + + /** + * Create an instance of UserPasswordAuthentication. + * + * @param userName + * User Name to send to SOCKS server. + * @param password + * Password to send to SOCKS server. + */ + public UserPasswordAuthentication(String userName, String password) { + this.userName = userName; + this.password = password; + formRequest(); + } + + /** + * Get the user name. + * + * @return User name. + */ + public String getUser() { + return userName; + } + + /** + * Get password + * + * @return Password + */ + public String getPassword() { + return password; + } + + /** + * Does User/Password authentication as defined in rfc1929. + * + * @return An array containnig in, out streams, or null if authentication + * fails. + */ + public Object[] doSocksAuthentication(int methodId, + java.net.Socket proxySocket) throws java.io.IOException { + + if (methodId != METHOD_ID) { + return null; + } + + final java.io.InputStream in = proxySocket.getInputStream(); + final java.io.OutputStream out = proxySocket.getOutputStream(); + + out.write(request); + final int version = in.read(); + if (version < 0) { + return null; // Server closed connection + } + final int status = in.read(); + if (status != 0) { + return null; // Server closed connection, or auth failed. + } + + return new Object[] { in, out }; + } + + // Private methods + // //////////////// + + /** Convert UserName password in to binary form, ready to be send to server */ + private void formRequest() { + final byte[] user_bytes = userName.getBytes(); + final byte[] password_bytes = password.getBytes(); + + request = new byte[3 + user_bytes.length + password_bytes.length]; + request[0] = (byte) 1; + request[1] = (byte) user_bytes.length; + System.arraycopy(user_bytes, 0, request, 2, user_bytes.length); + request[2 + user_bytes.length] = (byte) password_bytes.length; + System.arraycopy(password_bytes, 0, request, 3 + user_bytes.length, + password_bytes.length); + } +} diff --git a/src/com/runjva/sourceforge/jsocks/server/Ident.java b/src/com/runjva/sourceforge/jsocks/server/Ident.java new file mode 100644 index 00000000..7193f798 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/Ident.java @@ -0,0 +1,176 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.Socket; +import java.util.StringTokenizer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class Ident provides means to obtain user name of the owner of the socket on + * remote machine, providing remote machine runs identd daemon. + *

+ * To use it:

+   Socket s = ss.accept();
+   Ident id = new Ident(s);
+   if(id.successful) goUseUser(id.userName);
+   else handleIdentError(id.errorCode,id.errorMessage)
+   
+ */ +public class Ident { + + Logger log = LoggerFactory.getLogger(Ident.class); + + /** Error Message can be null. */ + public String errorMessage; + + /** Host type as returned by daemon, can be null, if error happened */ + public String hostType; + + /** User name as returned by the identd daemon, or null, if it failed */ + public String userName; + + /** + * If this is true then userName and hostType contain valid values. Else + * errorCode contain the error code, and errorMessage contains the + * corresponding message. + */ + public boolean successful; + + /** Error code */ + public int errorCode; + + /** Identd on port 113 can't be contacted */ + public static final int ERR_NO_CONNECT = 1; + + /** Connection timed out */ + public static final int ERR_TIMEOUT = 2; + + /** + * Identd daemon responded with ERROR, in this case errorMessage contains + * the string explanation, as send by the daemon. + */ + public static final int ERR_PROTOCOL = 3; + + /** + * When parsing server response protocol error happened. + */ + public static final int ERR_PROTOCOL_INCORRECT = 4; + + /** + * Maximum amount of time we should wait before dropping the connection to + * identd server.Setting it to 0 implies infinit timeout. + */ + public static final int connectionTimeout = 10000; + + /** + * Constructor tries to connect to Identd daemon on the host of the given + * socket, and retrieve user name of the owner of given socket connection on + * remote machine. After constructor returns public fields are initialised + * to whatever the server returned. + *

+ * If user name was successfully retrieved successful is set to true, and + * userName and hostType are set to whatever server returned. If however for + * some reason user name was not obtained, successful is set to false and + * errorCode contains the code explaining the reason of failure, and + * errorMessage contains human readable explanation. + *

+ * Constructor may block, for a while. + * + * @param s + * Socket whose ownership on remote end should be obtained. + */ + public Ident(Socket s) { + Socket sock = null; + successful = false; // We are pessimistic + + try { + sock = new Socket(s.getInetAddress(), 113); + sock.setSoTimeout(connectionTimeout); + final byte[] request = ("" + s.getPort() + " , " + s.getLocalPort() + "\r\n") + .getBytes(); + + sock.getOutputStream().write(request); + + final BufferedReader in = new BufferedReader(new InputStreamReader( + sock.getInputStream())); + + parseResponse(in.readLine()); + + } catch (final InterruptedIOException iioe) { + errorCode = ERR_TIMEOUT; + errorMessage = "Connection to identd timed out."; + } catch (final ConnectException ce) { + errorCode = ERR_NO_CONNECT; + errorMessage = "Connection to identd server failed."; + + } catch (final IOException ioe) { + errorCode = ERR_NO_CONNECT; + errorMessage = "" + ioe; + } finally { + try { + if (sock != null) { + sock.close(); + } + } catch (final IOException ioe) { + log.warn("Could not close socket", ioe); + } + } + } + + private void parseResponse(String response) { + if (response == null) { + errorCode = ERR_PROTOCOL_INCORRECT; + errorMessage = "Identd server closed connection."; + return; + } + + final StringTokenizer st = new StringTokenizer(response, ":"); + if (st.countTokens() < 3) { + errorCode = ERR_PROTOCOL_INCORRECT; + errorMessage = "Can't parse server response."; + return; + } + + st.nextToken(); // Discard first token, it's basically what we have send + final String command = st.nextToken().trim().toUpperCase(); + + if (command.equals("USERID") && (st.countTokens() >= 2)) { + successful = true; + hostType = st.nextToken().trim(); + userName = st.nextToken("").substring(1);// Get all that is left + } else if (command.equals("ERROR")) { + errorCode = ERR_PROTOCOL; + errorMessage = st.nextToken(); + } else { + errorCode = ERR_PROTOCOL_INCORRECT; + System.out.println("Opa!"); + errorMessage = "Can't parse server response."; + } + + } + + // ///////////////////////////////////////////// + // USED for Testing + /* + * public static void main(String[] args) throws IOException{ + * + * Socket s = null; s = new Socket("gp101-16", 1391); + * + * Ident id = new Ident(s); if(id.successful){ + * System.out.println("User: "+id.userName); + * System.out.println("HostType: "+id.hostType); }else{ + * System.out.println("ErrorCode: "+id.errorCode); + * System.out.println("ErrorMessage: "+id.errorMessage); + * + * } + * + * if(s!= null) s.close(); } // + */ + +} diff --git a/src/com/runjva/sourceforge/jsocks/server/IdentAuthenticator.java b/src/com/runjva/sourceforge/jsocks/server/IdentAuthenticator.java new file mode 100644 index 00000000..da040f55 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/IdentAuthenticator.java @@ -0,0 +1,182 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import com.runjva.sourceforge.jsocks.protocol.InetRange; +import com.runjva.sourceforge.jsocks.protocol.ProxyMessage; + +/** + * An implementation of socks.ServerAuthentication which provides simple + * authentication based on the host from which the connection is made and the + * name of the user on the remote machine, as reported by identd daemon on the + * remote machine. + *

+ * It can also be used to provide authentication based only on the contacting + * host address. + */ + +public class IdentAuthenticator extends ServerAuthenticatorBase { + /** Vector of InetRanges */ + Vector hosts; + + /** Vector of user hashes */ + Vector> users; + + String user; + + /** + * Constructs empty IdentAuthenticator. + */ + public IdentAuthenticator() { + hosts = new Vector(); + users = new Vector>(); + } + + /** + * Used to create instances returned from startSession. + * + * @param in + * Input stream. + * @param out + * OutputStream. + * @param user + * Username associated with this connection,could be null if name + * was not required. + */ + IdentAuthenticator(InputStream in, OutputStream out, String user) { + super(in, out); + this.user = user; + } + + /** + * Adds range of addresses from which connection is allowed. Hashtable users + * should contain user names as keys and anything as values (value is not + * used and will be ignored). + * + * @param hostRange + * Range of ip addresses from which connection is allowed. + * @param users + * Hashtable of users for whom connection is allowed, or null to + * indicate that anybody is allowed to connect from the hosts + * within given range. + */ + public synchronized void add(InetRange hostRange, Hashtable users) { + this.hosts.addElement(hostRange); + this.users.addElement(users); + } + + /** + * Grants permission only to those users, who connect from one of the hosts + * registered with add(InetRange,Hashtable) and whose names, as reported by + * identd daemon, are listed for the host the connection came from. + */ + public ServerAuthenticator startSession(Socket s) throws IOException { + + final int ind = getRangeIndex(s.getInetAddress()); + String user = null; + + // System.out.println("getRangeReturned:"+ind); + + if (ind < 0) { + return null; // Host is not on the list. + } + + final ServerAuthenticator serverAuthenticator = super.startSession(s); + final ServerAuthenticatorBase auth = (ServerAuthenticatorBase) serverAuthenticator; + + // System.out.println("super.startSession() returned:"+auth); + if (auth == null) { + return null; + } + + // do the authentication + + final Hashtable user_names = users.elementAt(ind); + + if (user_names != null) { // If need to do authentication + Ident ident; + ident = new Ident(s); + // If can't obtain user name, fail + if (!ident.successful) { + return null; + } + // If user name is not listed for this address, fail + if (!user_names.containsKey(ident.userName)) { + return null; + } + user = ident.userName; + } + return new IdentAuthenticator(auth.in, auth.out, user); + + } + + /** + * For SOCKS5 requests allways returns true. For SOCKS4 requests checks + * wether the user name supplied in the request corresponds to the name + * obtained from the ident daemon. + */ + public boolean checkRequest(ProxyMessage msg, java.net.Socket s) { + // If it's version 5 request, or if anybody is permitted, return true; + if ((msg.version == 5) || (user == null)) { + return true; + } + + if (msg.version != 4) { + return false; // Who knows? + } + + return user.equals(msg.user); + } + + /** Get String representaion of the IdentAuthenticator. */ + public String toString() { + String s = ""; + + for (int i = 0; i < hosts.size(); ++i) { + s += "(Range:" + hosts.elementAt(i) + "," + // + " Users:" + userNames(i) + ") "; + } + return s; + } + + // Private Methods + // //////////////// + private int getRangeIndex(InetAddress ip) { + int index = 0; + final Enumeration enumx = hosts.elements(); + while (enumx.hasMoreElements()) { + final InetRange ir = enumx.nextElement(); + if (ir.contains(ip)) { + return index; + } + index++; + } + return -1; // Not found + } + + private String userNames(int i) { + if (users.elementAt(i) == null) { + return "Everybody is permitted."; + } + + final Enumeration enumx = ((Hashtable) users.elementAt(i)) + .keys(); + if (!enumx.hasMoreElements()) { + return ""; + } + String s = enumx.nextElement().toString(); + while (enumx.hasMoreElements()) { + s += "; " + enumx.nextElement(); + } + + return s; + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticator.java b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticator.java new file mode 100644 index 00000000..c2451ac5 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticator.java @@ -0,0 +1,126 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.DatagramPacket; +import java.net.Socket; + +import com.runjva.sourceforge.jsocks.protocol.ProxyMessage; +import com.runjva.sourceforge.jsocks.protocol.UDPEncapsulation; + +/** + * Classes implementing this interface should provide socks server with + * authentication and authorization of users. + **/ +public interface ServerAuthenticator { + + /** + * This method is called when a new connection accepted by the server. + *

+ * At this point no data have been extracted from the connection. It is + * responsibility of this method to ensure that the next byte in the stream + * after this method have been called is the first byte of the socks request + * message. For SOCKSv4 there is no authentication data and the first byte + * in the stream is part of the request. With SOCKSv5 however there is an + * authentication data first. It is expected that implementaions will + * process this authentication data. + *

+ * If authentication was successful an instance of ServerAuthentication + * should be returned, it later will be used by the server to perform + * authorization and some other things. If authentication fails null should + * be returned, or an exception may be thrown. + * + * @param s + * Accepted Socket. + * @return An instance of ServerAuthenticator to be used for this connection + * or null + */ + ServerAuthenticator startSession(Socket s) throws IOException; + + /** + * This method should return input stream which should be used on the + * accepted socket. + *

+ * SOCKSv5 allows to have multiple authentication methods, and these methods + * might require some kind of transformations being made on the data. + *

+ * This method is called on the object returned from the startSession + * function. + */ + InputStream getInputStream(); + + /** + * This method should return output stream to use to write to the accepted + * socket. + *

+ * SOCKSv5 allows to have multiple authentication methods, and these methods + * might require some kind of transformations being made on the data. + *

+ * This method is called on the object returned from the startSession + * function. + */ + OutputStream getOutputStream(); + + /** + * This method should return UDPEncapsulation, which should be used on the + * datagrams being send in/out. + *

+ * If no transformation should be done on the datagrams, this method should + * return null. + *

+ * This method is called on the object returned from the startSession + * function. + */ + + UDPEncapsulation getUdpEncapsulation(); + + /** + * This method is called when a request have been read. + *

+ * Implementation should decide wether to grant request or not. Returning + * true implies granting the request, false means request should be + * rejected. + *

+ * This method is called on the object returned from the startSession + * function. + * + * @param msg + * Request message. + * @return true to grant request, false to reject it. + */ + boolean checkRequest(ProxyMessage msg); + + /** + * This method is called when datagram is received by the server. + *

+ * Implementaions should decide wether it should be forwarded or dropped. It + * is expecteed that implementation will use datagram address and port + * information to make a decision, as well as anything else. Address and + * port of the datagram are always correspond to remote machine. It is + * either destination or source address. If out is true address is + * destination address, else it is a source address, address of the machine + * from which datagram have been received for the client. + *

+ * Implementaions should return true if the datagram is to be forwarded, and + * false if the datagram should be dropped. + *

+ * This method is called on the object returned from the startSession + * function. + * + * @param out + * If true the datagram is being send out(from the client), + * otherwise it is an incoming datagram. + * @return True to forward datagram false drop it silently. + */ + boolean checkRequest(DatagramPacket dp, boolean out); + + /** + * This method is called when session is completed. Either due to normal + * termination or due to any error condition. + *

+ * This method is called on the object returned from the startSession + * function. + */ + void endSession(); +} diff --git a/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorBase.java b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorBase.java new file mode 100644 index 00000000..c67a89f6 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorBase.java @@ -0,0 +1,187 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.Socket; + +import com.runjva.sourceforge.jsocks.protocol.ProxyMessage; +import com.runjva.sourceforge.jsocks.protocol.UDPEncapsulation; + +/** + * An implementation of ServerAuthenticator, which does not do any + * authentication. + *

+ * Warning!!
+ * Should not be used on machines which are not behind the firewall. + *

+ * It is only provided to make implementing other authentication schemes easier. + *
+ * For Example:

+   class MyAuth extends socks.server.ServerAuthenticator{
+    ...
+    public ServerAuthenticator startSession(java.net.Socket s){
+      if(!checkHost(s.getInetAddress()) return null;
+      return super.startSession(s);
+    }
+
+    boolean checkHost(java.net.Inetaddress addr){
+      boolean allow;
+      //Do it somehow
+      return allow;
+    }
+   }
+
+ */ +public abstract class ServerAuthenticatorBase implements ServerAuthenticator { + + static final byte[] socks5response = { 5, 0 }; + + InputStream in; + OutputStream out; + + /** + * Creates new instance of the ServerAuthenticatorNone. + */ + public ServerAuthenticatorBase() { + this.in = null; + this.out = null; + } + + /** + * Constructs new ServerAuthenticatorNone object suitable for returning from + * the startSession function. + * + * @param in + * Input stream to return from getInputStream method. + * @param out + * Output stream to return from getOutputStream method. + */ + public ServerAuthenticatorBase(InputStream in, OutputStream out) { + this.in = in; + this.out = out; + } + + /** + * Grants access to everyone.Removes authentication related bytes from the + * stream, when a SOCKS5 connection is being made, selects an authentication + * NONE. + */ + public ServerAuthenticator startSession(Socket s) throws IOException { + + final PushbackInputStream in = new PushbackInputStream(s + .getInputStream()); + final OutputStream out = s.getOutputStream(); + + final int version = in.read(); + if (version == 5) { + if (!selectSocks5Authentication(in, out, 0)) { + return null; + } + } else if (version == 4) { + // Else it is the request message already, version 4 + in.unread(version); + } else { + return null; + } + + return new ServerAuthenticatorNone(in, out); + } + + /** + * Get input stream. + * + * @return Input stream speciefied in the constructor. + */ + public InputStream getInputStream() { + return in; + } + + /** + * Get output stream. + * + * @return Output stream speciefied in the constructor. + */ + public OutputStream getOutputStream() { + return out; + } + + /** + * Allways returns null. + * + * @return null + */ + public UDPEncapsulation getUdpEncapsulation() { + return null; + } + + /** + * Allways returns true. + */ + public boolean checkRequest(ProxyMessage msg) { + return true; + } + + /** + * Allways returns true. + */ + public boolean checkRequest(java.net.DatagramPacket dp, boolean out) { + return true; + } + + /** + * Does nothing. + */ + public void endSession() { + } + + /** + * Convinience routine for selecting SOCKSv5 authentication. + *

+ * This method reads in authentication methods that client supports, checks + * wether it supports given method. If it does, the notification method is + * written back to client, that this method have been chosen for + * authentication. If given method was not found, authentication failure + * message is send to client ([5,FF]). + * + * @param in + * Input stream, version byte should be removed from the stream + * before calling this method. + * @param out + * Output stream. + * @param methodId + * Method which should be selected. + * @return true if methodId was found, false otherwise. + */ + static public boolean selectSocks5Authentication(InputStream in, + OutputStream out, int methodId) throws IOException { + + final int num_methods = in.read(); + if (num_methods <= 0) { + return false; + } + final byte method_ids[] = new byte[num_methods]; + final byte response[] = new byte[2]; + boolean found = false; + + response[0] = (byte) 5; // SOCKS version + response[1] = (byte) 0xFF; // Not found, we are pessimistic + + int bread = 0; // bytes read so far + while (bread < num_methods) { + bread += in.read(method_ids, bread, num_methods - bread); + } + + for (int i = 0; i < num_methods; ++i) { + if (method_ids[i] == methodId) { + found = true; + response[1] = (byte) methodId; + break; + } + } + + out.write(response); + return found; + } +} diff --git a/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorNone.java b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorNone.java new file mode 100644 index 00000000..0e976773 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/ServerAuthenticatorNone.java @@ -0,0 +1,16 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Simplest possible ServerAuthenticator implementation. Extends common base. + * + */ +public class ServerAuthenticatorNone extends ServerAuthenticatorBase { + + public ServerAuthenticatorNone(InputStream in, OutputStream out) { + super(in, out); + } + +} diff --git a/src/com/runjva/sourceforge/jsocks/server/UserPasswordAuthenticator.java b/src/com/runjva/sourceforge/jsocks/server/UserPasswordAuthenticator.java new file mode 100644 index 00000000..7d54b45b --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/UserPasswordAuthenticator.java @@ -0,0 +1,82 @@ +package com.runjva.sourceforge.jsocks.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * This class implements SOCKS5 User/Password authentication scheme as defined + * in rfc1929,the server side of it. (see docs/rfc1929.txt) + */ +public class UserPasswordAuthenticator extends ServerAuthenticatorBase { + + static final int METHOD_ID = 2; + + UserValidation validator; + + /** + * Construct a new UserPasswordAuthentication object, with given + * UserVlaidation scheme. + * + * @param v + * UserValidation to use for validating users. + */ + public UserPasswordAuthenticator(UserValidation validator) { + this.validator = validator; + } + + public ServerAuthenticator startSession(Socket s) throws IOException { + final InputStream in = s.getInputStream(); + final OutputStream out = s.getOutputStream(); + + if (in.read() != 5) { + return null; // Drop non version 5 messages. + } + + if (!selectSocks5Authentication(in, out, METHOD_ID)) { + return null; + } + if (!doUserPasswordAuthentication(s, in, out)) { + return null; + } + + return new ServerAuthenticatorNone(in, out); + } + + // Private Methods + // //////////////// + + private boolean doUserPasswordAuthentication(Socket s, InputStream in, + OutputStream out) throws IOException { + final int version = in.read(); + if (version != 1) { + return false; + } + + final int ulen = in.read(); + if (ulen < 0) { + return false; + } + + final byte[] user = new byte[ulen]; + in.read(user); + final int plen = in.read(); + if (plen < 0) { + return false; + } + final byte[] password = new byte[plen]; + in.read(password); + + if (validator.isUserValid(new String(user), new String(password), s)) { + // System.out.println("user valid"); + out.write(new byte[] { 1, 0 }); + } else { + // System.out.println("user invalid"); + out.write(new byte[] { 1, 1 }); + return false; + } + + return true; + } +} diff --git a/src/com/runjva/sourceforge/jsocks/server/UserValidation.java b/src/com/runjva/sourceforge/jsocks/server/UserValidation.java new file mode 100644 index 00000000..d0b52290 --- /dev/null +++ b/src/com/runjva/sourceforge/jsocks/server/UserValidation.java @@ -0,0 +1,24 @@ +package com.runjva.sourceforge.jsocks.server; + +/** + * Interface which provides for user validation, based on user name password and + * where it connects from. + */ +public interface UserValidation { + /** + * Implementations of this interface are expected to use some or all of the + * information provided plus any information they can extract from other + * sources to decide wether given user should be allowed access to SOCKS + * server, or whatever you use it for. + * + * @return true to indicate user is valid, false otherwise. + * @param username + * User whom implementation should validate. + * @param password + * Password this user provided. + * @param connection + * Socket which user used to connect to the server. + */ + boolean isUserValid(String username, String password, + java.net.Socket connection); +} diff --git a/src/org/sandroproxy/ony/OrbotApp.java b/src/org/sandroproxy/ony/OrbotApp.java new file mode 100644 index 00000000..fdc3b895 --- /dev/null +++ b/src/org/sandroproxy/ony/OrbotApp.java @@ -0,0 +1,69 @@ +package org.sandroproxy.ony; + +import java.util.Locale; + +import org.torproject.android.TorConstants; +import org.torproject.android.service.TorServiceUtils; + +import android.app.Application; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.preference.PreferenceManager; + +public class OrbotApp extends Application implements TorConstants +{ + + private Locale locale; + private final static String DEFAULT_LOCALE = "en"; + private SharedPreferences settings; + + @Override + public void onCreate() { + super.onCreate(); + + settings = TorServiceUtils.getSharedPrefs(getApplicationContext()); + + Configuration config = getResources().getConfiguration(); + + String lang = settings.getString(PREF_DEFAULT_LOCALE, DEFAULT_LOCALE); + + if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang)) + { + if (lang.equals("xx")) + { + locale = Locale.getDefault(); + + } + else + locale = new Locale(lang); + + Locale.setDefault(locale); + + Configuration myConfig = new Configuration(config); + myConfig.locale = locale; + + getResources().updateConfiguration(myConfig, getResources().getDisplayMetrics()); + } + + + } + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + + String lang = settings.getString(PREF_DEFAULT_LOCALE, DEFAULT_LOCALE); + + if (! "".equals(lang) && ! newConfig.locale.getLanguage().equals(lang)) + { + locale = new Locale(lang); + Locale.setDefault(locale); + + Configuration myConfig = new Configuration(newConfig); + myConfig.locale = locale; + + getResources().updateConfiguration(myConfig, getResources().getDisplayMetrics()); + } + } +} From 90db557ceccb8ccd7ad0412ebdb6409d95bf1577 Mon Sep 17 00:00:00 2001 From: SandroB Date: Sun, 25 Jan 2015 12:01:00 +0100 Subject: [PATCH 3/5] clone of badvpn for dns fix --- external/badvpn_dnsfix | 1 + jni/Android.mk | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 external/badvpn_dnsfix diff --git a/external/badvpn_dnsfix b/external/badvpn_dnsfix new file mode 160000 index 00000000..c639c8c3 --- /dev/null +++ b/external/badvpn_dnsfix @@ -0,0 +1 @@ +Subproject commit c639c8c305bd51fbe178f1b83f8c1ba8730a7e17 diff --git a/jni/Android.mk b/jni/Android.mk index 4eb1e785..178e692c 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -1,3 +1,3 @@ ##include ../OriginalDest/Android.mk -include ./external/badvpn/Android.mk +include ./external/badvpn_dnsfix/Android.mk ##include ../kalium-jni/jni/Android.mk From 72a017369b8bdb9ed2494f7a24fdb1bcb00f0455 Mon Sep 17 00:00:00 2001 From: SandroB Date: Sun, 25 Jan 2015 12:06:02 +0100 Subject: [PATCH 4/5] delete as it should not be as subproject --- external/badvpn_dnsfix | 1 - 1 file changed, 1 deletion(-) delete mode 160000 external/badvpn_dnsfix diff --git a/external/badvpn_dnsfix b/external/badvpn_dnsfix deleted file mode 160000 index c639c8c3..00000000 --- a/external/badvpn_dnsfix +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c639c8c305bd51fbe178f1b83f8c1ba8730a7e17 From 1464901fe3fed48066683d225265fc5deba58d40 Mon Sep 17 00:00:00 2001 From: SandroB Date: Sun, 25 Jan 2015 12:08:34 +0100 Subject: [PATCH 5/5] added badvpn as local folder --- external/badvpn_dns/Android.mk | 75 + external/badvpn_dns/CMakeLists.txt | 408 ++ external/badvpn_dns/COPYING | 24 + external/badvpn_dns/ChangeLog | 216 + external/badvpn_dns/INSTALL | 76 + external/badvpn_dns/INSTALL-WINDOWS | 72 + external/badvpn_dns/arpprobe/BArpProbe.c | 359 ++ external/badvpn_dns/arpprobe/BArpProbe.h | 80 + external/badvpn_dns/arpprobe/CMakeLists.txt | 1 + external/badvpn_dns/badvpn.7 | 324 ++ external/badvpn_dns/base/BLog.c | 96 + external/badvpn_dns/base/BLog.h | 402 ++ external/badvpn_dns/base/BLog_syslog.c | 150 + external/badvpn_dns/base/BLog_syslog.h | 42 + external/badvpn_dns/base/BMutex.h | 101 + external/badvpn_dns/base/BPending.c | 205 + external/badvpn_dns/base/BPending.h | 250 + external/badvpn_dns/base/BPending_list.h | 4 + external/badvpn_dns/base/CMakeLists.txt | 13 + external/badvpn_dns/base/DebugObject.c | 39 + external/badvpn_dns/base/DebugObject.h | 147 + external/badvpn_dns/blog_channels.txt | 145 + external/badvpn_dns/blog_generator/blog.php | 121 + .../blog_generator/blog_functions.php | 35 + external/badvpn_dns/bproto/BProto.h | 85 + .../bproto_generator/ProtoParser.lime | 99 + .../bproto_generator/ProtoParser.php | 560 ++ .../badvpn_dns/bproto_generator/bproto.php | 115 + .../bproto_generator/bproto_functions.php | 777 +++ external/badvpn_dns/client/CMakeLists.txt | 30 + external/badvpn_dns/client/DPReceive.c | 324 ++ external/badvpn_dns/client/DPReceive.h | 98 + external/badvpn_dns/client/DPRelay.c | 307 ++ external/badvpn_dns/client/DPRelay.h | 89 + external/badvpn_dns/client/DataProto.c | 566 ++ external/badvpn_dns/client/DataProto.h | 237 + .../client/DataProtoKeepaliveSource.c | 72 + .../client/DataProtoKeepaliveSource.h | 73 + external/badvpn_dns/client/DatagramPeerIO.c | 425 ++ external/badvpn_dns/client/DatagramPeerIO.h | 271 + .../client/FragmentProtoAssembler.c | 469 ++ .../client/FragmentProtoAssembler.h | 134 + .../client/FragmentProtoAssembler_tree.h | 9 + .../client/FragmentProtoDisassembler.c | 229 + .../client/FragmentProtoDisassembler.h | 109 + external/badvpn_dns/client/FrameDecider.c | 795 +++ external/badvpn_dns/client/FrameDecider.h | 196 + .../client/FrameDecider_groups_tree.h | 9 + .../client/FrameDecider_macs_tree.h | 9 + .../client/FrameDecider_multicast_tree.h | 9 + external/badvpn_dns/client/PasswordListener.c | 374 ++ external/badvpn_dns/client/PasswordListener.h | 156 + external/badvpn_dns/client/PeerChat.c | 433 ++ external/badvpn_dns/client/PeerChat.h | 123 + external/badvpn_dns/client/SCOutmsgEncoder.c | 104 + external/badvpn_dns/client/SCOutmsgEncoder.h | 76 + external/badvpn_dns/client/SPProtoDecoder.c | 398 ++ external/badvpn_dns/client/SPProtoDecoder.h | 171 + external/badvpn_dns/client/SPProtoEncoder.c | 436 ++ external/badvpn_dns/client/SPProtoEncoder.h | 172 + .../badvpn_dns/client/SimpleStreamBuffer.c | 144 + .../badvpn_dns/client/SimpleStreamBuffer.h | 52 + .../badvpn_dns/client/SinglePacketSource.c | 85 + .../badvpn_dns/client/SinglePacketSource.h | 73 + external/badvpn_dns/client/StreamPeerIO.c | 712 +++ external/badvpn_dns/client/StreamPeerIO.h | 222 + external/badvpn_dns/client/badvpn-client.8 | 316 ++ external/badvpn_dns/client/client.c | 2997 ++++++++++ external/badvpn_dns/client/client.h | 193 + .../cmake/modules/COPYING-CMAKE-SCRIPTS | 22 + .../badvpn_dns/cmake/modules/FindGLIB2.cmake | 52 + .../cmake/modules/FindLibraryWithDebug.cmake | 113 + .../badvpn_dns/cmake/modules/FindNSPR.cmake | 57 + .../badvpn_dns/cmake/modules/FindNSS.cmake | 57 + .../cmake/modules/FindOpenSSL.cmake | 72 + external/badvpn_dns/compile-tun2sock.sh | 112 + external/badvpn_dns/compile-udpgw.sh | 84 + external/badvpn_dns/dhcpclient/BDHCPClient.c | 340 ++ external/badvpn_dns/dhcpclient/BDHCPClient.h | 87 + .../badvpn_dns/dhcpclient/BDHCPClientCore.c | 860 +++ .../badvpn_dns/dhcpclient/BDHCPClientCore.h | 114 + external/badvpn_dns/dhcpclient/CMakeLists.txt | 10 + .../badvpn_dns/dhcpclient/DHCPIpUdpDecoder.c | 137 + .../badvpn_dns/dhcpclient/DHCPIpUdpDecoder.h | 49 + .../badvpn_dns/dhcpclient/DHCPIpUdpEncoder.c | 119 + .../badvpn_dns/dhcpclient/DHCPIpUdpEncoder.h | 49 + external/badvpn_dns/dostest/CMakeLists.txt | 10 + external/badvpn_dns/dostest/StreamBuffer.c | 147 + external/badvpn_dns/dostest/StreamBuffer.h | 70 + .../badvpn_dns/dostest/dostest-attacker.c | 512 ++ external/badvpn_dns/dostest/dostest-server.c | 567 ++ external/badvpn_dns/examples/CMakeLists.txt | 97 + .../badvpn_dns/examples/FastPacketSource.h | 79 + .../badvpn_dns/examples/RandomPacketSink.h | 116 + .../badvpn_dns/examples/TimerPacketSink.h | 97 + external/badvpn_dns/examples/arpprobe_test.c | 131 + external/badvpn_dns/examples/bavl_test.c | 129 + .../badvpn_dns/examples/bencryption_bench.c | 146 + .../badvpn_dns/examples/bprocess_example.c | 140 + external/badvpn_dns/examples/brandom2_test.c | 65 + external/badvpn_dns/examples/btimer_example.c | 84 + external/badvpn_dns/examples/cavl_test.c | 285 + external/badvpn_dns/examples/cavl_test_tree.h | 23 + .../badvpn_dns/examples/dhcpclient_test.c | 159 + .../badvpn_dns/examples/emscripten_test.c | 71 + external/badvpn_dns/examples/fairqueue_test.c | 145 + .../badvpn_dns/examples/fairqueue_test2.c | 93 + .../badvpn_dns/examples/indexedlist_test.c | 95 + external/badvpn_dns/examples/ipaddr6_test.c | 169 + .../badvpn_dns/examples/ncd_parser_test.c | 294 + .../badvpn_dns/examples/ncd_tokenizer_test.c | 149 + .../examples/ncd_value_parser_test.c | 78 + .../examples/ncdinterfacemonitor_test.c | 150 + .../badvpn_dns/examples/ncdudevmanager_test.c | 161 + .../badvpn_dns/examples/ncdudevmonitor_test.c | 152 + external/badvpn_dns/examples/ncdval_test.c | 380 ++ .../badvpn_dns/examples/ncdvalcons_test.c | 111 + .../badvpn_dns/examples/parse_number_test.c | 130 + external/badvpn_dns/examples/predicate_test.c | 116 + external/badvpn_dns/examples/savl_test.c | 135 + external/badvpn_dns/examples/savl_test_tree.h | 9 + external/badvpn_dns/examples/stdin_input.c | 138 + external/badvpn_dns/examples/substring_test.c | 204 + external/badvpn_dns/fix_flex.php | 10 + external/badvpn_dns/flooder/CMakeLists.txt | 7 + external/badvpn_dns/flooder/flooder.c | 671 +++ external/badvpn_dns/flooder/flooder.h | 37 + external/badvpn_dns/flow/BufferWriter.c | 112 + external/badvpn_dns/flow/BufferWriter.h | 107 + external/badvpn_dns/flow/CMakeLists.txt | 31 + external/badvpn_dns/flow/LineBuffer.c | 140 + external/badvpn_dns/flow/LineBuffer.h | 54 + external/badvpn_dns/flow/PacketBuffer.c | 131 + external/badvpn_dns/flow/PacketBuffer.h | 77 + external/badvpn_dns/flow/PacketCopier.c | 136 + external/badvpn_dns/flow/PacketCopier.h | 90 + .../badvpn_dns/flow/PacketPassConnector.c | 125 + .../badvpn_dns/flow/PacketPassConnector.h | 102 + .../badvpn_dns/flow/PacketPassFairQueue.c | 405 ++ .../badvpn_dns/flow/PacketPassFairQueue.h | 204 + .../flow/PacketPassFairQueue_tree.h | 7 + .../badvpn_dns/flow/PacketPassFifoQueue.c | 241 + .../badvpn_dns/flow/PacketPassFifoQueue.h | 76 + .../badvpn_dns/flow/PacketPassInterface.c | 68 + .../badvpn_dns/flow/PacketPassInterface.h | 236 + external/badvpn_dns/flow/PacketPassNotifier.c | 103 + external/badvpn_dns/flow/PacketPassNotifier.h | 99 + .../badvpn_dns/flow/PacketPassPriorityQueue.c | 283 + .../badvpn_dns/flow/PacketPassPriorityQueue.h | 192 + .../flow/PacketPassPriorityQueue_tree.h | 7 + external/badvpn_dns/flow/PacketProtoDecoder.c | 182 + external/badvpn_dns/flow/PacketProtoDecoder.h | 96 + external/badvpn_dns/flow/PacketProtoEncoder.c | 101 + external/badvpn_dns/flow/PacketProtoEncoder.h | 80 + external/badvpn_dns/flow/PacketProtoFlow.c | 82 + external/badvpn_dns/flow/PacketProtoFlow.h | 83 + external/badvpn_dns/flow/PacketRecvBlocker.c | 99 + external/badvpn_dns/flow/PacketRecvBlocker.h | 90 + .../badvpn_dns/flow/PacketRecvConnector.c | 123 + .../badvpn_dns/flow/PacketRecvConnector.h | 102 + .../badvpn_dns/flow/PacketRecvInterface.c | 56 + .../badvpn_dns/flow/PacketRecvInterface.h | 170 + external/badvpn_dns/flow/PacketRouter.c | 129 + external/badvpn_dns/flow/PacketRouter.h | 126 + external/badvpn_dns/flow/PacketStreamSender.c | 111 + external/badvpn_dns/flow/PacketStreamSender.h | 83 + external/badvpn_dns/flow/RouteBuffer.c | 256 + external/badvpn_dns/flow/RouteBuffer.h | 139 + external/badvpn_dns/flow/SinglePacketBuffer.c | 87 + external/badvpn_dns/flow/SinglePacketBuffer.h | 75 + external/badvpn_dns/flow/SinglePacketSender.c | 72 + external/badvpn_dns/flow/SinglePacketSender.h | 82 + .../badvpn_dns/flow/SingleStreamReceiver.c | 82 + .../badvpn_dns/flow/SingleStreamReceiver.h | 53 + external/badvpn_dns/flow/SingleStreamSender.c | 82 + external/badvpn_dns/flow/SingleStreamSender.h | 53 + external/badvpn_dns/flow/StreamPacketSender.c | 90 + external/badvpn_dns/flow/StreamPacketSender.h | 77 + .../badvpn_dns/flow/StreamPassConnector.c | 120 + .../badvpn_dns/flow/StreamPassConnector.h | 98 + .../badvpn_dns/flow/StreamPassInterface.c | 56 + .../badvpn_dns/flow/StreamPassInterface.h | 165 + .../badvpn_dns/flow/StreamRecvConnector.c | 120 + .../badvpn_dns/flow/StreamRecvConnector.h | 98 + .../badvpn_dns/flow/StreamRecvInterface.c | 56 + .../badvpn_dns/flow/StreamRecvInterface.h | 165 + external/badvpn_dns/flowextra/CMakeLists.txt | 5 + external/badvpn_dns/flowextra/KeepaliveIO.c | 112 + external/badvpn_dns/flowextra/KeepaliveIO.h | 88 + .../flowextra/PacketPassInactivityMonitor.c | 131 + .../flowextra/PacketPassInactivityMonitor.h | 124 + external/badvpn_dns/generate_files | 51 + .../generated/NCDConfigParser_parse.c | 1890 +++++++ .../generated/NCDConfigParser_parse.h | 22 + .../generated/NCDConfigParser_parse.out | 950 ++++ .../generated/NCDConfigParser_parse.y | 718 +++ .../badvpn_dns/generated/NCDValParser_parse.c | 1119 ++++ .../badvpn_dns/generated/NCDValParser_parse.h | 7 + .../generated/NCDValParser_parse.out | 217 + .../badvpn_dns/generated/NCDValParser_parse.y | 202 + .../badvpn_dns/generated/bison_BPredicate.c | 2168 ++++++++ .../badvpn_dns/generated/bison_BPredicate.h | 114 + .../generated/blog_channel_BArpProbe.h | 4 + .../generated/blog_channel_BConnection.h | 4 + .../generated/blog_channel_BDHCPClient.h | 4 + .../generated/blog_channel_BDHCPClientCore.h | 4 + .../generated/blog_channel_BDatagram.h | 4 + .../generated/blog_channel_BEncryption.h | 4 + .../generated/blog_channel_BInputProcess.h | 4 + .../generated/blog_channel_BLockReactor.h | 4 + .../generated/blog_channel_BNetwork.h | 4 + .../generated/blog_channel_BPredicate.h | 4 + .../generated/blog_channel_BProcess.h | 4 + .../generated/blog_channel_BReactor.h | 4 + .../generated/blog_channel_BSSLConnection.h | 4 + .../generated/blog_channel_BSignal.h | 4 + .../generated/blog_channel_BSocksClient.h | 4 + .../badvpn_dns/generated/blog_channel_BTap.h | 4 + .../generated/blog_channel_BThreadSignal.h | 4 + .../generated/blog_channel_BThreadWork.h | 4 + .../badvpn_dns/generated/blog_channel_BTime.h | 4 + .../generated/blog_channel_BUnixSignal.h | 4 + .../generated/blog_channel_DPReceive.h | 4 + .../generated/blog_channel_DPRelay.h | 4 + .../generated/blog_channel_DataProto.h | 4 + .../generated/blog_channel_DatagramPeerIO.h | 4 + .../blog_channel_FragmentProtoAssembler.h | 4 + .../generated/blog_channel_FrameDecider.h | 4 + .../generated/blog_channel_LineBuffer.h | 4 + .../generated/blog_channel_Listener.h | 4 + .../generated/blog_channel_NCDBuildProgram.h | 4 + .../generated/blog_channel_NCDConfigParser.h | 4 + .../blog_channel_NCDConfigTokenizer.h | 4 + .../generated/blog_channel_NCDIfConfig.h | 4 + .../blog_channel_NCDInterfaceMonitor.h | 4 + .../generated/blog_channel_NCDModuleIndex.h | 4 + .../generated/blog_channel_NCDModuleProcess.h | 4 + .../generated/blog_channel_NCDPlaceholderDb.h | 4 + .../generated/blog_channel_NCDRequest.h | 4 + .../generated/blog_channel_NCDRequestClient.h | 4 + .../generated/blog_channel_NCDRfkillMonitor.h | 4 + .../generated/blog_channel_NCDUdevCache.h | 4 + .../generated/blog_channel_NCDUdevManager.h | 4 + .../generated/blog_channel_NCDUdevMonitor.h | 4 + .../blog_channel_NCDUdevMonitorParser.h | 4 + .../generated/blog_channel_NCDVal.h | 4 + .../generated/blog_channel_NCDValGenerator.h | 4 + .../generated/blog_channel_NCDValParser.h | 4 + .../generated/blog_channel_PRStreamSink.h | 4 + .../generated/blog_channel_PRStreamSource.h | 4 + .../blog_channel_PacketProtoDecoder.h | 4 + .../generated/blog_channel_PasswordListener.h | 4 + .../generated/blog_channel_PeerChat.h | 4 + .../generated/blog_channel_SPProtoDecoder.h | 4 + .../generated/blog_channel_ServerConnection.h | 4 + .../generated/blog_channel_SocksUdpGwClient.h | 4 + .../generated/blog_channel_StreamPeerIO.h | 4 + .../generated/blog_channel_UdpGwClient.h | 4 + .../badvpn_dns/generated/blog_channel_addr.h | 4 + .../generated/blog_channel_client.h | 4 + .../generated/blog_channel_dostest_attacker.h | 4 + .../generated/blog_channel_dostest_server.h | 4 + .../generated/blog_channel_flooder.h | 4 + .../badvpn_dns/generated/blog_channel_lwip.h | 4 + .../badvpn_dns/generated/blog_channel_ncd.h | 4 + .../generated/blog_channel_ncd_alias.h | 4 + .../generated/blog_channel_ncd_arithmetic.h | 4 + .../generated/blog_channel_ncd_assert.h | 4 + .../generated/blog_channel_ncd_backtrack.h | 4 + .../generated/blog_channel_ncd_blocker.h | 4 + .../generated/blog_channel_ncd_buffer.h | 4 + .../generated/blog_channel_ncd_call2.h | 4 + .../generated/blog_channel_ncd_choose.h | 4 + .../generated/blog_channel_ncd_concat.h | 4 + .../generated/blog_channel_ncd_daemon.h | 4 + .../generated/blog_channel_ncd_depend.h | 4 + .../generated/blog_channel_ncd_depend_scope.h | 4 + .../blog_channel_ncd_dynamic_depend.h | 4 + .../generated/blog_channel_ncd_exit.h | 4 + .../generated/blog_channel_ncd_explode.h | 4 + .../generated/blog_channel_ncd_file.h | 4 + .../generated/blog_channel_ncd_file_open.h | 4 + .../generated/blog_channel_ncd_foreach.h | 4 + .../generated/blog_channel_ncd_from_string.h | 4 + .../generated/blog_channel_ncd_getargs.h | 4 + .../generated/blog_channel_ncd_getenv.h | 4 + .../generated/blog_channel_ncd_if.h | 4 + .../generated/blog_channel_ncd_imperative.h | 4 + .../generated/blog_channel_ncd_implode.h | 4 + .../generated/blog_channel_ncd_index.h | 4 + .../generated/blog_channel_ncd_list.h | 4 + .../generated/blog_channel_ncd_load_module.h | 4 + .../generated/blog_channel_ncd_log.h | 4 + .../generated/blog_channel_ncd_log_msg.h | 4 + .../generated/blog_channel_ncd_logical.h | 4 + .../generated/blog_channel_ncd_multidepend.h | 4 + .../blog_channel_ncd_net_backend_badvpn.h | 4 + .../blog_channel_ncd_net_backend_rfkill.h | 4 + .../blog_channel_ncd_net_backend_waitdevice.h | 4 + .../blog_channel_ncd_net_backend_waitlink.h | 4 + ...g_channel_ncd_net_backend_wpa_supplicant.h | 4 + .../generated/blog_channel_ncd_net_dns.h | 4 + .../generated/blog_channel_ncd_net_iptables.h | 4 + .../blog_channel_ncd_net_ipv4_addr.h | 4 + ...log_channel_ncd_net_ipv4_addr_in_network.h | 4 + .../blog_channel_ncd_net_ipv4_arp_probe.h | 4 + .../blog_channel_ncd_net_ipv4_dhcp.h | 4 + .../blog_channel_ncd_net_ipv4_route.h | 4 + .../blog_channel_ncd_net_ipv6_addr.h | 4 + ...log_channel_ncd_net_ipv6_addr_in_network.h | 4 + .../blog_channel_ncd_net_ipv6_route.h | 4 + ...g_channel_ncd_net_ipv6_wait_dynamic_addr.h | 4 + .../generated/blog_channel_ncd_net_up.h | 4 + .../blog_channel_ncd_net_watch_interfaces.h | 4 + .../generated/blog_channel_ncd_netmask.h | 4 + .../generated/blog_channel_ncd_ondemand.h | 4 + .../generated/blog_channel_ncd_parse.h | 4 + .../generated/blog_channel_ncd_print.h | 4 + .../blog_channel_ncd_process_manager.h | 4 + .../generated/blog_channel_ncd_reboot.h | 4 + .../generated/blog_channel_ncd_ref.h | 4 + .../generated/blog_channel_ncd_regex_match.h | 4 + .../generated/blog_channel_ncd_request.h | 4 + .../generated/blog_channel_ncd_run.h | 4 + .../generated/blog_channel_ncd_runonce.h | 4 + .../generated/blog_channel_ncd_sleep.h | 4 + .../generated/blog_channel_ncd_socket.h | 4 + .../generated/blog_channel_ncd_spawn.h | 4 + .../generated/blog_channel_ncd_strcmp.h | 4 + .../generated/blog_channel_ncd_substr.h | 4 + .../generated/blog_channel_ncd_sys_evdev.h | 4 + .../blog_channel_ncd_sys_request_client.h | 4 + .../blog_channel_ncd_sys_request_server.h | 4 + .../blog_channel_ncd_sys_start_process.h | 4 + .../blog_channel_ncd_sys_watch_directory.h | 4 + .../blog_channel_ncd_sys_watch_input.h | 4 + .../blog_channel_ncd_sys_watch_usb.h | 4 + .../generated/blog_channel_ncd_timer.h | 4 + .../generated/blog_channel_ncd_to_string.h | 4 + .../generated/blog_channel_ncd_try.h | 4 + .../generated/blog_channel_ncd_value.h | 4 + .../generated/blog_channel_ncd_valuemetic.h | 4 + .../generated/blog_channel_ncd_var.h | 4 + .../generated/blog_channel_nsskey.h | 4 + .../generated/blog_channel_server.h | 4 + .../generated/blog_channel_tun2socks.h | 4 + .../badvpn_dns/generated/blog_channel_udpgw.h | 4 + .../generated/blog_channels_defines.h | 146 + .../badvpn_dns/generated/blog_channels_list.h | 145 + external/badvpn_dns/generated/bproto_addr.h | 675 +++ .../badvpn_dns/generated/bproto_bproto_test.h | 1029 ++++ .../badvpn_dns/generated/bproto_msgproto.h | 2122 +++++++ .../badvpn_dns/generated/flex_BPredicate.c | 2143 ++++++++ .../badvpn_dns/generated/flex_BPredicate.h | 350 ++ external/badvpn_dns/lemon/lemon.c | 4889 +++++++++++++++++ external/badvpn_dns/lemon/lempar.c | 842 +++ external/badvpn_dns/lime/HOWTO | 70 + .../badvpn_dns/lime/flex_token_stream.php | 34 + external/badvpn_dns/lime/lemon.c | 4588 ++++++++++++++++ external/badvpn_dns/lime/lime.bootstrap | 31 + external/badvpn_dns/lime/lime.php | 910 +++ external/badvpn_dns/lime/lime_scan_tokens.l | 121 + external/badvpn_dns/lime/metagrammar | 58 + external/badvpn_dns/lime/parse_engine.php | 252 + external/badvpn_dns/lime/set.so.php | 29 + external/badvpn_dns/lwip/CHANGELOG | 3396 ++++++++++++ external/badvpn_dns/lwip/CMakeLists.txt | 27 + external/badvpn_dns/lwip/COPYING | 33 + external/badvpn_dns/lwip/FILES | 4 + external/badvpn_dns/lwip/README | 89 + external/badvpn_dns/lwip/UPGRADING | 144 + external/badvpn_dns/lwip/custom/arch/cc.h | 96 + external/badvpn_dns/lwip/custom/arch/perf.h | 36 + external/badvpn_dns/lwip/custom/lwipopts.h | 70 + external/badvpn_dns/lwip/custom/sys.c | 37 + external/badvpn_dns/lwip/doc/FILES | 6 + external/badvpn_dns/lwip/doc/contrib.txt | 63 + external/badvpn_dns/lwip/doc/rawapi.txt | 511 ++ external/badvpn_dns/lwip/doc/savannah.txt | 135 + external/badvpn_dns/lwip/doc/snmp_agent.txt | 181 + external/badvpn_dns/lwip/doc/sys_arch.txt | 267 + external/badvpn_dns/lwip/lwip-base-version | 1 + external/badvpn_dns/lwip/src/FILES | 13 + external/badvpn_dns/lwip/src/api/api_lib.c | 788 +++ external/badvpn_dns/lwip/src/api/api_msg.c | 1610 ++++++ external/badvpn_dns/lwip/src/api/err.c | 75 + external/badvpn_dns/lwip/src/api/netbuf.c | 245 + external/badvpn_dns/lwip/src/api/netdb.c | 353 ++ external/badvpn_dns/lwip/src/api/netifapi.c | 160 + external/badvpn_dns/lwip/src/api/sockets.c | 2555 +++++++++ external/badvpn_dns/lwip/src/api/tcpip.c | 492 ++ external/badvpn_dns/lwip/src/core/def.c | 108 + external/badvpn_dns/lwip/src/core/dhcp.c | 1771 ++++++ external/badvpn_dns/lwip/src/core/dns.c | 988 ++++ .../badvpn_dns/lwip/src/core/inet_chksum.c | 545 ++ external/badvpn_dns/lwip/src/core/init.c | 345 ++ .../badvpn_dns/lwip/src/core/ipv4/autoip.c | 528 ++ external/badvpn_dns/lwip/src/core/ipv4/icmp.c | 338 ++ external/badvpn_dns/lwip/src/core/ipv4/igmp.c | 805 +++ external/badvpn_dns/lwip/src/core/ipv4/ip4.c | 924 ++++ .../badvpn_dns/lwip/src/core/ipv4/ip4_addr.c | 312 ++ .../badvpn_dns/lwip/src/core/ipv4/ip_frag.c | 863 +++ external/badvpn_dns/lwip/src/core/ipv6/README | 1 + .../badvpn_dns/lwip/src/core/ipv6/dhcp6.c | 50 + .../badvpn_dns/lwip/src/core/ipv6/ethip6.c | 193 + .../badvpn_dns/lwip/src/core/ipv6/icmp6.c | 337 ++ .../badvpn_dns/lwip/src/core/ipv6/inet6.c | 51 + external/badvpn_dns/lwip/src/core/ipv6/ip6.c | 1034 ++++ .../badvpn_dns/lwip/src/core/ipv6/ip6_addr.c | 251 + .../badvpn_dns/lwip/src/core/ipv6/ip6_frag.c | 697 +++ external/badvpn_dns/lwip/src/core/ipv6/mld6.c | 580 ++ external/badvpn_dns/lwip/src/core/ipv6/nd6.c | 1787 ++++++ external/badvpn_dns/lwip/src/core/mem.c | 659 +++ external/badvpn_dns/lwip/src/core/memp.c | 485 ++ external/badvpn_dns/lwip/src/core/netif.c | 918 ++++ external/badvpn_dns/lwip/src/core/pbuf.c | 1179 ++++ external/badvpn_dns/lwip/src/core/raw.c | 422 ++ .../badvpn_dns/lwip/src/core/snmp/asn1_dec.c | 657 +++ .../badvpn_dns/lwip/src/core/snmp/asn1_enc.c | 611 ++ external/badvpn_dns/lwip/src/core/snmp/mib2.c | 4146 ++++++++++++++ .../lwip/src/core/snmp/mib_structs.c | 1174 ++++ .../badvpn_dns/lwip/src/core/snmp/msg_in.c | 1453 +++++ .../badvpn_dns/lwip/src/core/snmp/msg_out.c | 678 +++ external/badvpn_dns/lwip/src/core/stats.c | 181 + external/badvpn_dns/lwip/src/core/sys.c | 68 + external/badvpn_dns/lwip/src/core/tcp.c | 1852 +++++++ external/badvpn_dns/lwip/src/core/tcp_in.c | 1666 ++++++ external/badvpn_dns/lwip/src/core/tcp_out.c | 1499 +++++ external/badvpn_dns/lwip/src/core/timers.c | 546 ++ external/badvpn_dns/lwip/src/core/udp.c | 1151 ++++ .../lwip/src/include/ipv4/lwip/autoip.h | 118 + .../lwip/src/include/ipv4/lwip/icmp.h | 125 + .../lwip/src/include/ipv4/lwip/igmp.h | 106 + .../lwip/src/include/ipv4/lwip/inet.h | 107 + .../lwip/src/include/ipv4/lwip/ip4.h | 146 + .../lwip/src/include/ipv4/lwip/ip4_addr.h | 244 + .../lwip/src/include/ipv4/lwip/ip_frag.h | 91 + .../lwip/src/include/ipv6/lwip/dhcp6.h | 58 + .../lwip/src/include/ipv6/lwip/ethip6.h | 68 + .../lwip/src/include/ipv6/lwip/icmp6.h | 152 + .../lwip/src/include/ipv6/lwip/inet6.h | 92 + .../lwip/src/include/ipv6/lwip/ip6.h | 197 + .../lwip/src/include/ipv6/lwip/ip6_addr.h | 286 + .../lwip/src/include/ipv6/lwip/ip6_frag.h | 102 + .../lwip/src/include/ipv6/lwip/mld6.h | 118 + .../lwip/src/include/ipv6/lwip/nd6.h | 369 ++ .../badvpn_dns/lwip/src/include/lwip/api.h | 338 ++ .../lwip/src/include/lwip/api_msg.h | 177 + .../badvpn_dns/lwip/src/include/lwip/arch.h | 217 + .../badvpn_dns/lwip/src/include/lwip/debug.h | 99 + .../badvpn_dns/lwip/src/include/lwip/def.h | 123 + .../badvpn_dns/lwip/src/include/lwip/dhcp.h | 242 + .../badvpn_dns/lwip/src/include/lwip/dns.h | 124 + .../badvpn_dns/lwip/src/include/lwip/err.h | 85 + .../lwip/src/include/lwip/inet_chksum.h | 112 + .../badvpn_dns/lwip/src/include/lwip/init.h | 72 + .../badvpn_dns/lwip/src/include/lwip/ip.h | 254 + .../lwip/src/include/lwip/ip_addr.h | 130 + .../badvpn_dns/lwip/src/include/lwip/mem.h | 123 + .../badvpn_dns/lwip/src/include/lwip/memp.h | 116 + .../lwip/src/include/lwip/memp_std.h | 135 + .../badvpn_dns/lwip/src/include/lwip/netbuf.h | 112 + .../badvpn_dns/lwip/src/include/lwip/netdb.h | 124 + .../badvpn_dns/lwip/src/include/lwip/netif.h | 393 ++ .../lwip/src/include/lwip/netifapi.h | 108 + .../badvpn_dns/lwip/src/include/lwip/opt.h | 2417 ++++++++ .../badvpn_dns/lwip/src/include/lwip/pbuf.h | 185 + .../badvpn_dns/lwip/src/include/lwip/raw.h | 131 + .../badvpn_dns/lwip/src/include/lwip/sio.h | 141 + .../badvpn_dns/lwip/src/include/lwip/snmp.h | 367 ++ .../lwip/src/include/lwip/snmp_asn1.h | 101 + .../lwip/src/include/lwip/snmp_msg.h | 315 ++ .../lwip/src/include/lwip/snmp_structs.h | 268 + .../lwip/src/include/lwip/sockets.h | 411 ++ .../badvpn_dns/lwip/src/include/lwip/stats.h | 347 ++ .../badvpn_dns/lwip/src/include/lwip/sys.h | 336 ++ .../badvpn_dns/lwip/src/include/lwip/tcp.h | 400 ++ .../lwip/src/include/lwip/tcp_impl.h | 508 ++ .../badvpn_dns/lwip/src/include/lwip/tcpip.h | 179 + .../badvpn_dns/lwip/src/include/lwip/timers.h | 100 + .../badvpn_dns/lwip/src/include/lwip/udp.h | 215 + .../lwip/src/include/netif/etharp.h | 223 + .../lwip/src/include/netif/ppp_oe.h | 190 + .../lwip/src/include/netif/slipif.h | 81 + .../badvpn_dns/lwip/src/include/posix/netdb.h | 33 + .../lwip/src/include/posix/sys/socket.h | 33 + external/badvpn_dns/lwip/src/netif/FILES | 29 + external/badvpn_dns/lwip/src/netif/etharp.c | 1413 +++++ .../badvpn_dns/lwip/src/netif/ethernetif.c | 322 ++ external/badvpn_dns/lwip/src/netif/ppp/auth.c | 1334 +++++ external/badvpn_dns/lwip/src/netif/ppp/auth.h | 111 + external/badvpn_dns/lwip/src/netif/ppp/chap.c | 908 +++ external/badvpn_dns/lwip/src/netif/ppp/chap.h | 150 + .../badvpn_dns/lwip/src/netif/ppp/chpms.c | 396 ++ .../badvpn_dns/lwip/src/netif/ppp/chpms.h | 64 + external/badvpn_dns/lwip/src/netif/ppp/fsm.c | 890 +++ external/badvpn_dns/lwip/src/netif/ppp/fsm.h | 157 + external/badvpn_dns/lwip/src/netif/ppp/ipcp.c | 1411 +++++ external/badvpn_dns/lwip/src/netif/ppp/ipcp.h | 106 + external/badvpn_dns/lwip/src/netif/ppp/lcp.c | 2066 +++++++ external/badvpn_dns/lwip/src/netif/ppp/lcp.h | 151 + .../badvpn_dns/lwip/src/netif/ppp/magic.c | 80 + .../badvpn_dns/lwip/src/netif/ppp/magic.h | 63 + external/badvpn_dns/lwip/src/netif/ppp/md5.c | 320 ++ external/badvpn_dns/lwip/src/netif/ppp/md5.h | 55 + external/badvpn_dns/lwip/src/netif/ppp/pap.c | 628 +++ external/badvpn_dns/lwip/src/netif/ppp/pap.h | 118 + external/badvpn_dns/lwip/src/netif/ppp/ppp.c | 2052 +++++++ external/badvpn_dns/lwip/src/netif/ppp/ppp.h | 201 + .../badvpn_dns/lwip/src/netif/ppp/ppp_impl.h | 363 ++ .../badvpn_dns/lwip/src/netif/ppp/ppp_oe.c | 1132 ++++ .../badvpn_dns/lwip/src/netif/ppp/pppdebug.h | 73 + .../badvpn_dns/lwip/src/netif/ppp/randm.c | 249 + .../badvpn_dns/lwip/src/netif/ppp/randm.h | 81 + .../badvpn_dns/lwip/src/netif/ppp/readme.txt | 21 + external/badvpn_dns/lwip/src/netif/ppp/vj.c | 652 +++ external/badvpn_dns/lwip/src/netif/ppp/vj.h | 156 + external/badvpn_dns/lwip/src/netif/slipif.c | 546 ++ .../badvpn_dns/lwip/test/unit/core/test_mem.c | 73 + .../badvpn_dns/lwip/test/unit/core/test_mem.h | 8 + .../lwip/test/unit/core/test_pbuf.c | 73 + .../lwip/test/unit/core/test_pbuf.h | 8 + .../lwip/test/unit/dhcp/test_dhcp.c | 916 +++ .../lwip/test/unit/dhcp/test_dhcp.h | 8 + .../lwip/test/unit/etharp/test_etharp.c | 262 + .../lwip/test/unit/etharp/test_etharp.h | 8 + .../badvpn_dns/lwip/test/unit/lwip_check.h | 37 + .../lwip/test/unit/lwip_unittests.c | 49 + external/badvpn_dns/lwip/test/unit/lwipopts.h | 53 + .../lwip/test/unit/tcp/tcp_helper.c | 303 + .../lwip/test/unit/tcp/tcp_helper.h | 52 + .../badvpn_dns/lwip/test/unit/tcp/test_tcp.c | 671 +++ .../badvpn_dns/lwip/test/unit/tcp/test_tcp.h | 8 + .../lwip/test/unit/tcp/test_tcp_oos.c | 958 ++++ .../lwip/test/unit/tcp/test_tcp_oos.h | 8 + .../badvpn_dns/lwip/test/unit/udp/test_udp.c | 68 + .../badvpn_dns/lwip/test/unit/udp/test_udp.h | 8 + external/badvpn_dns/misc/BRefTarget.h | 114 + external/badvpn_dns/misc/Utf16Decoder.h | 113 + external/badvpn_dns/misc/Utf16Encoder.h | 67 + external/badvpn_dns/misc/Utf8Decoder.h | 143 + external/badvpn_dns/misc/Utf8Encoder.h | 81 + external/badvpn_dns/misc/arp_proto.h | 60 + external/badvpn_dns/misc/array_length.h | 35 + external/badvpn_dns/misc/balign.h | 76 + external/badvpn_dns/misc/balloc.h | 248 + external/badvpn_dns/misc/blimits.h | 60 + external/badvpn_dns/misc/bsize.h | 117 + external/badvpn_dns/misc/bsort.h | 69 + external/badvpn_dns/misc/bstring.h | 140 + external/badvpn_dns/misc/byteorder.h | 196 + external/badvpn_dns/misc/cmdline.h | 181 + external/badvpn_dns/misc/compare.h | 37 + external/badvpn_dns/misc/concat_strings.h | 85 + external/badvpn_dns/misc/cstring.h | 347 ++ external/badvpn_dns/misc/dead.h | 134 + external/badvpn_dns/misc/debug.h | 142 + external/badvpn_dns/misc/debugcounter.h | 118 + external/badvpn_dns/misc/debugerror.h | 90 + external/badvpn_dns/misc/dhcp_proto.h | 131 + external/badvpn_dns/misc/ethernet_proto.h | 52 + external/badvpn_dns/misc/exparray.h | 101 + external/badvpn_dns/misc/expstring.h | 161 + external/badvpn_dns/misc/find_char.h | 58 + external/badvpn_dns/misc/find_program.h | 103 + external/badvpn_dns/misc/get_iface_info.h | 110 + external/badvpn_dns/misc/grow_array.h | 139 + external/badvpn_dns/misc/hashfun.h | 60 + external/badvpn_dns/misc/igmp_proto.h | 97 + external/badvpn_dns/misc/ipaddr.h | 218 + external/badvpn_dns/misc/ipaddr6.h | 400 ++ external/badvpn_dns/misc/ipv4_proto.h | 145 + external/badvpn_dns/misc/ipv6_proto.h | 86 + external/badvpn_dns/misc/loggers_string.h | 43 + external/badvpn_dns/misc/loglevel.h | 80 + external/badvpn_dns/misc/maxalign.h | 53 + external/badvpn_dns/misc/merge.h | 36 + external/badvpn_dns/misc/minmax.h | 56 + external/badvpn_dns/misc/modadd.h | 59 + external/badvpn_dns/misc/mswsock.h | 229 + external/badvpn_dns/misc/nonblocking.h | 51 + external/badvpn_dns/misc/nsskey.h | 118 + external/badvpn_dns/misc/offset.h | 51 + .../badvpn_dns/misc/open_standard_streams.h | 54 + external/badvpn_dns/misc/overflow.h | 66 + external/badvpn_dns/misc/packed.h | 51 + external/badvpn_dns/misc/parse_number.h | 304 + external/badvpn_dns/misc/print_macros.h | 98 + external/badvpn_dns/misc/read_file.h | 98 + external/badvpn_dns/misc/read_write_int.h | 181 + external/badvpn_dns/misc/socks_proto.h | 118 + external/badvpn_dns/misc/sslsocket.h | 48 + external/badvpn_dns/misc/stdbuf_cmdline.h | 92 + external/badvpn_dns/misc/strdup.h | 86 + external/badvpn_dns/misc/string_begins_with.h | 96 + external/badvpn_dns/misc/substring.h | 81 + external/badvpn_dns/misc/udp_proto.h | 170 + external/badvpn_dns/misc/unicode_funcs.h | 232 + external/badvpn_dns/misc/version.h | 41 + external/badvpn_dns/misc/write_file.h | 104 + .../badvpn_dns/ncd-request/CMakeLists.txt | 9 + external/badvpn_dns/ncd-request/ncd-request.c | 224 + external/badvpn_dns/ncd/CMakeLists.txt | 211 + external/badvpn_dns/ncd/NCDAst.c | 1022 ++++ external/badvpn_dns/ncd/NCDAst.h | 237 + external/badvpn_dns/ncd/NCDBuildProgram.c | 316 ++ external/badvpn_dns/ncd/NCDBuildProgram.h | 49 + external/badvpn_dns/ncd/NCDConfigParser.c | 214 + external/badvpn_dns/ncd/NCDConfigParser.h | 40 + .../badvpn_dns/ncd/NCDConfigParser_parse.y | 718 +++ external/badvpn_dns/ncd/NCDConfigTokenizer.c | 321 ++ external/badvpn_dns/ncd/NCDConfigTokenizer.h | 64 + external/badvpn_dns/ncd/NCDInterpProcess.c | 497 ++ external/badvpn_dns/ncd/NCDInterpProcess.h | 100 + external/badvpn_dns/ncd/NCDInterpProg.c | 140 + external/badvpn_dns/ncd/NCDInterpProg.h | 63 + external/badvpn_dns/ncd/NCDInterpProg_hash.h | 12 + external/badvpn_dns/ncd/NCDInterpreter.c | 1356 +++++ external/badvpn_dns/ncd/NCDInterpreter.h | 156 + external/badvpn_dns/ncd/NCDMethodIndex.c | 272 + external/badvpn_dns/ncd/NCDMethodIndex.h | 135 + external/badvpn_dns/ncd/NCDMethodIndex_hash.h | 12 + external/badvpn_dns/ncd/NCDModule.c | 625 +++ external/badvpn_dns/ncd/NCDModule.h | 1011 ++++ external/badvpn_dns/ncd/NCDModuleIndex.c | 372 ++ external/badvpn_dns/ncd/NCDModuleIndex.h | 86 + .../badvpn_dns/ncd/NCDModuleIndex_mhash.h | 12 + external/badvpn_dns/ncd/NCDObject.c | 40 + external/badvpn_dns/ncd/NCDObject.h | 356 ++ external/badvpn_dns/ncd/NCDPlaceholderDb.c | 127 + external/badvpn_dns/ncd/NCDPlaceholderDb.h | 95 + external/badvpn_dns/ncd/NCDStringIndex.c | 262 + external/badvpn_dns/ncd/NCDStringIndex.h | 83 + external/badvpn_dns/ncd/NCDStringIndex_hash.h | 13 + external/badvpn_dns/ncd/NCDSugar.c | 253 + external/badvpn_dns/ncd/NCDSugar.h | 38 + external/badvpn_dns/ncd/NCDVal.c | 2065 +++++++ external/badvpn_dns/ncd/NCDVal.h | 857 +++ external/badvpn_dns/ncd/NCDValCons.c | 283 + external/badvpn_dns/ncd/NCDValCons.h | 176 + external/badvpn_dns/ncd/NCDValGenerator.c | 193 + external/badvpn_dns/ncd/NCDValGenerator.h | 40 + external/badvpn_dns/ncd/NCDValParser.c | 225 + external/badvpn_dns/ncd/NCDValParser.h | 50 + external/badvpn_dns/ncd/NCDValParser_parse.y | 202 + external/badvpn_dns/ncd/NCDVal_maptree.h | 15 + external/badvpn_dns/ncd/README | 386 ++ external/badvpn_dns/ncd/emncd.c | 137 + external/badvpn_dns/ncd/emncd.html | 320 ++ .../badvpn_dns/ncd/examples/dbus_start.ncd | 82 + .../ncd/examples/dhcpd.conf.template | 11 + .../ncd/examples/directory_updater.ncd | 72 + external/badvpn_dns/ncd/examples/events.ncd | 101 + .../ncd/examples/igmpproxy.conf.template | 10 + .../ncd/examples/make_dhcp_config.ncd | 27 + .../ncd/examples/make_igmpproxy_config.ncd | 53 + external/badvpn_dns/ncd/examples/network.ncd | 123 + .../badvpn_dns/ncd/examples/onoff_server.ncdi | 82 + .../ncd/examples/onoff_server_test.ncd | 20 + .../badvpn_dns/ncd/examples/router/README | 36 + .../ncd/examples/router/add-port-forwarding | 43 + .../ncd/examples/router/dhcp_server.ncdi | 60 + .../ncd/examples/router/list-port-forwardings | 61 + .../badvpn_dns/ncd/examples/router/ncd.conf | 6 + .../ncd/examples/router/network.ncdi | 356 ++ .../router/network_control_server.ncdi | 96 + .../ncd/examples/router/port_forwarding.ncdi | 170 + .../badvpn_dns/ncd/examples/router/pppoe.ncdi | 296 + .../examples/router/remove-port-forwarding | 43 + .../ncd/examples/router/unbound.ncdi | 42 + .../ncd/examples/tcp_echo_client.ncd | 35 + .../ncd/examples/tcp_echo_server.ncd | 40 + external/badvpn_dns/ncd/extra/BEventLock.c | 146 + external/badvpn_dns/ncd/extra/BEventLock.h | 127 + .../badvpn_dns/ncd/extra/NCDBProcessOpts.c | 154 + .../badvpn_dns/ncd/extra/NCDBProcessOpts.h | 54 + external/badvpn_dns/ncd/extra/NCDBuf.c | 123 + external/badvpn_dns/ncd/extra/NCDBuf.h | 61 + external/badvpn_dns/ncd/extra/NCDIfConfig.c | 483 ++ external/badvpn_dns/ncd/extra/NCDIfConfig.h | 70 + .../ncd/extra/NCDInterfaceMonitor.c | 446 ++ .../ncd/extra/NCDInterfaceMonitor.h | 160 + .../badvpn_dns/ncd/extra/NCDRequestClient.c | 647 +++ .../badvpn_dns/ncd/extra/NCDRequestClient.h | 111 + .../badvpn_dns/ncd/extra/NCDRfkillMonitor.c | 117 + .../badvpn_dns/ncd/extra/NCDRfkillMonitor.h | 53 + external/badvpn_dns/ncd/extra/address_utils.h | 280 + external/badvpn_dns/ncd/extra/build_cmdline.c | 111 + external/badvpn_dns/ncd/extra/build_cmdline.h | 38 + .../badvpn_dns/ncd/extra/make_fast_names.h | 154 + external/badvpn_dns/ncd/extra/value_utils.h | 174 + external/badvpn_dns/ncd/include_linux_input.c | 1 + external/badvpn_dns/ncd/make_name_indices.h | 104 + external/badvpn_dns/ncd/modules/alias.c | 148 + external/badvpn_dns/ncd/modules/arithmetic.c | 404 ++ external/badvpn_dns/ncd/modules/assert.c | 105 + external/badvpn_dns/ncd/modules/backtrack.c | 103 + external/badvpn_dns/ncd/modules/blocker.c | 353 ++ external/badvpn_dns/ncd/modules/buffer.c | 619 +++ .../ncd/modules/buffer_chunks_tree.h | 9 + external/badvpn_dns/ncd/modules/call2.c | 498 ++ external/badvpn_dns/ncd/modules/choose.c | 145 + .../badvpn_dns/ncd/modules/command_template.c | 218 + .../badvpn_dns/ncd/modules/command_template.h | 62 + external/badvpn_dns/ncd/modules/concat.c | 189 + external/badvpn_dns/ncd/modules/daemon.c | 296 + external/badvpn_dns/ncd/modules/depend.c | 452 ++ .../badvpn_dns/ncd/modules/depend_scope.c | 466 ++ .../badvpn_dns/ncd/modules/dynamic_depend.c | 548 ++ .../badvpn_dns/ncd/modules/event_template.c | 184 + .../badvpn_dns/ncd/modules/event_template.h | 64 + external/badvpn_dns/ncd/modules/exit.c | 91 + external/badvpn_dns/ncd/modules/explode.c | 232 + external/badvpn_dns/ncd/modules/file.c | 350 ++ external/badvpn_dns/ncd/modules/file_open.c | 585 ++ external/badvpn_dns/ncd/modules/foreach.c | 715 +++ external/badvpn_dns/ncd/modules/from_string.c | 125 + external/badvpn_dns/ncd/modules/getargs.c | 98 + external/badvpn_dns/ncd/modules/getenv.c | 153 + external/badvpn_dns/ncd/modules/if.c | 103 + external/badvpn_dns/ncd/modules/imperative.c | 324 ++ external/badvpn_dns/ncd/modules/implode.c | 155 + external/badvpn_dns/ncd/modules/index.c | 164 + external/badvpn_dns/ncd/modules/list.c | 871 +++ external/badvpn_dns/ncd/modules/load_module.c | 313 ++ external/badvpn_dns/ncd/modules/log.c | 285 + external/badvpn_dns/ncd/modules/logical.c | 160 + external/badvpn_dns/ncd/modules/modules.h | 210 + external/badvpn_dns/ncd/modules/multidepend.c | 401 ++ .../ncd/modules/net_backend_badvpn.c | 281 + .../ncd/modules/net_backend_rfkill.c | 216 + .../ncd/modules/net_backend_waitdevice.c | 187 + .../ncd/modules/net_backend_waitlink.c | 155 + .../ncd/modules/net_backend_wpa_supplicant.c | 573 ++ external/badvpn_dns/ncd/modules/net_dns.c | 434 ++ .../badvpn_dns/ncd/modules/net_iptables.c | 749 +++ .../badvpn_dns/ncd/modules/net_ipv4_addr.c | 148 + .../ncd/modules/net_ipv4_addr_in_network.c | 173 + .../ncd/modules/net_ipv4_arp_probe.c | 212 + .../badvpn_dns/ncd/modules/net_ipv4_dhcp.c | 351 ++ .../badvpn_dns/ncd/modules/net_ipv4_route.c | 211 + .../badvpn_dns/ncd/modules/net_ipv6_addr.c | 148 + .../ncd/modules/net_ipv6_addr_in_network.c | 168 + .../badvpn_dns/ncd/modules/net_ipv6_route.c | 213 + .../ncd/modules/net_ipv6_wait_dynamic_addr.c | 201 + external/badvpn_dns/ncd/modules/net_up.c | 119 + .../ncd/modules/net_watch_interfaces.c | 474 ++ external/badvpn_dns/ncd/modules/netmask.c | 263 + external/badvpn_dns/ncd/modules/ondemand.c | 372 ++ external/badvpn_dns/ncd/modules/parse.c | 392 ++ external/badvpn_dns/ncd/modules/print.c | 207 + .../badvpn_dns/ncd/modules/process_manager.c | 538 ++ external/badvpn_dns/ncd/modules/reboot.c | 103 + external/badvpn_dns/ncd/modules/ref.c | 215 + external/badvpn_dns/ncd/modules/regex_match.c | 369 ++ external/badvpn_dns/ncd/modules/run.c | 187 + external/badvpn_dns/ncd/modules/runonce.c | 331 ++ external/badvpn_dns/ncd/modules/sleep.c | 178 + external/badvpn_dns/ncd/modules/socket.c | 1057 ++++ external/badvpn_dns/ncd/modules/spawn.c | 410 ++ external/badvpn_dns/ncd/modules/strcmp.c | 107 + external/badvpn_dns/ncd/modules/substr.c | 167 + external/badvpn_dns/ncd/modules/sys_evdev.c | 348 ++ .../ncd/modules/sys_request_client.c | 646 +++ .../ncd/modules/sys_request_server.c | 792 +++ .../ncd/modules/sys_start_process.c | 1266 +++++ .../ncd/modules/sys_watch_directory.c | 425 ++ .../badvpn_dns/ncd/modules/sys_watch_input.c | 455 ++ .../badvpn_dns/ncd/modules/sys_watch_usb.c | 421 ++ external/badvpn_dns/ncd/modules/timer.c | 146 + external/badvpn_dns/ncd/modules/to_string.c | 116 + external/badvpn_dns/ncd/modules/try.c | 302 + external/badvpn_dns/ncd/modules/value.c | 2102 +++++++ .../badvpn_dns/ncd/modules/value_maptree.h | 11 + external/badvpn_dns/ncd/modules/valuemetic.c | 219 + external/badvpn_dns/ncd/modules/var.c | 163 + external/badvpn_dns/ncd/ncd.c | 463 ++ external/badvpn_dns/ncd/ncd.h | 37 + external/badvpn_dns/ncd/parse_linux_input.sh | 94 + external/badvpn_dns/ncd/static_strings.h | 70 + .../badvpn_dns/ncd/tests/addr_in_network.ncd | 60 + external/badvpn_dns/ncd/tests/alias.ncd | 48 + external/badvpn_dns/ncd/tests/arithmetic.ncd | 69 + .../badvpn_dns/ncd/tests/backtracking.ncd | 31 + external/badvpn_dns/ncd/tests/buffer.ncd | 54 + external/badvpn_dns/ncd/tests/call.ncd | 18 + external/badvpn_dns/ncd/tests/concat.ncd | 19 + external/badvpn_dns/ncd/tests/depend.ncd | 64 + .../badvpn_dns/ncd/tests/depend_scope.ncd | 31 + .../badvpn_dns/ncd/tests/escape_and_nulls.ncd | 38 + external/badvpn_dns/ncd/tests/explode.ncd | 23 + external/badvpn_dns/ncd/tests/foreach.ncd | 35 + external/badvpn_dns/ncd/tests/if.ncd | 38 + external/badvpn_dns/ncd/tests/implode.ncd | 15 + external/badvpn_dns/ncd/tests/include.ncd | 16 + .../ncd/tests/include_included.ncdi | 5 + .../ncd/tests/include_included2.ncdi | 5 + external/badvpn_dns/ncd/tests/logical.ncd | 46 + external/badvpn_dns/ncd/tests/multidepend.ncd | 30 + external/badvpn_dns/ncd/tests/netmask.ncd | 15 + external/badvpn_dns/ncd/tests/parse.ncd | 85 + .../badvpn_dns/ncd/tests/process_manager.ncd | 112 + external/badvpn_dns/ncd/tests/regex.ncd | 48 + external/badvpn_dns/ncd/tests/run_tests | 38 + external/badvpn_dns/ncd/tests/strings.ncd | 47 + external/badvpn_dns/ncd/tests/substr.ncd | 37 + external/badvpn_dns/ncd/tests/turing.ncd | 138 + external/badvpn_dns/ncd/tests/value.ncd | 258 + .../badvpn_dns/ncd/tests/value_substr.ncd | 25 + .../badvpn_dns/nspr_support/BSSLConnection.c | 1024 ++++ .../badvpn_dns/nspr_support/BSSLConnection.h | 116 + .../badvpn_dns/nspr_support/CMakeLists.txt | 5 + .../badvpn_dns/nspr_support/DummyPRFileDesc.c | 176 + .../badvpn_dns/nspr_support/DummyPRFileDesc.h | 61 + external/badvpn_dns/predicate/BPredicate.c | 284 + external/badvpn_dns/predicate/BPredicate.h | 177 + external/badvpn_dns/predicate/BPredicate.l | 83 + external/badvpn_dns/predicate/BPredicate.y | 345 ++ .../predicate/BPredicate_internal.h | 154 + .../badvpn_dns/predicate/BPredicate_parser.h | 47 + external/badvpn_dns/predicate/CMakeLists.txt | 6 + .../predicate/LexMemoryBufferInput.h | 86 + external/badvpn_dns/protocol/addr.bproto | 11 + external/badvpn_dns/protocol/addr.h | 207 + external/badvpn_dns/protocol/dataproto.h | 91 + external/badvpn_dns/protocol/fragmentproto.h | 100 + external/badvpn_dns/protocol/msgproto.bproto | 43 + external/badvpn_dns/protocol/msgproto.h | 76 + external/badvpn_dns/protocol/packetproto.h | 68 + external/badvpn_dns/protocol/requestproto.h | 50 + external/badvpn_dns/protocol/scproto.h | 266 + external/badvpn_dns/protocol/spproto.h | 195 + external/badvpn_dns/protocol/udpgw_proto.h | 84 + external/badvpn_dns/random/BRandom2.c | 90 + external/badvpn_dns/random/BRandom2.h | 50 + external/badvpn_dns/random/CMakeLists.txt | 1 + external/badvpn_dns/scripts/cmake | 8 + external/badvpn_dns/scripts/copy_nss | 23 + external/badvpn_dns/scripts/toolchain.cmake | 6 + external/badvpn_dns/security/BEncryption.c | 240 + external/badvpn_dns/security/BEncryption.h | 175 + external/badvpn_dns/security/BHash.c | 69 + external/badvpn_dns/security/BHash.h | 80 + external/badvpn_dns/security/BRandom.c | 42 + external/badvpn_dns/security/BRandom.h | 49 + external/badvpn_dns/security/BSecurity.c | 149 + external/badvpn_dns/security/BSecurity.h | 60 + external/badvpn_dns/security/CMakeLists.txt | 10 + external/badvpn_dns/security/OTPCalculator.c | 118 + external/badvpn_dns/security/OTPCalculator.h | 96 + external/badvpn_dns/security/OTPChecker.c | 297 + external/badvpn_dns/security/OTPChecker.h | 148 + external/badvpn_dns/security/OTPGenerator.c | 159 + external/badvpn_dns/security/OTPGenerator.h | 134 + external/badvpn_dns/server/CMakeLists.txt | 12 + external/badvpn_dns/server/badvpn-server.8 | 190 + external/badvpn_dns/server/server.c | 2394 ++++++++ external/badvpn_dns/server/server.h | 186 + .../server_connection/CMakeLists.txt | 5 + .../server_connection/SCKeepaliveSource.c | 69 + .../server_connection/SCKeepaliveSource.h | 72 + .../server_connection/ServerConnection.c | 669 +++ .../server_connection/ServerConnection.h | 289 + .../badvpn_dns/socksclient/BSocksClient.c | 608 ++ .../badvpn_dns/socksclient/BSocksClient.h | 147 + .../badvpn_dns/socksclient/CMakeLists.txt | 1 + external/badvpn_dns/stringmap/BStringMap.c | 198 + external/badvpn_dns/stringmap/BStringMap.h | 57 + external/badvpn_dns/stringmap/CMakeLists.txt | 1 + external/badvpn_dns/structure/BAVL.h | 797 +++ external/badvpn_dns/structure/CAvl.h | 36 + external/badvpn_dns/structure/CAvl_decl.h | 77 + external/badvpn_dns/structure/CAvl_footer.h | 113 + external/badvpn_dns/structure/CAvl_header.h | 141 + external/badvpn_dns/structure/CAvl_impl.h | 949 ++++ external/badvpn_dns/structure/CHash.h | 39 + external/badvpn_dns/structure/CHash_decl.h | 59 + external/badvpn_dns/structure/CHash_footer.h | 74 + external/badvpn_dns/structure/CHash_header.h | 78 + external/badvpn_dns/structure/CHash_impl.h | 312 ++ external/badvpn_dns/structure/ChunkBuffer2.h | 317 ++ external/badvpn_dns/structure/IndexedList.h | 225 + .../badvpn_dns/structure/IndexedList_tree.h | 15 + external/badvpn_dns/structure/LinkedList0.h | 202 + external/badvpn_dns/structure/LinkedList1.h | 275 + external/badvpn_dns/structure/LinkedList3.h | 362 ++ external/badvpn_dns/structure/SAvl.h | 40 + external/badvpn_dns/structure/SAvl_decl.h | 73 + external/badvpn_dns/structure/SAvl_footer.h | 89 + external/badvpn_dns/structure/SAvl_header.h | 93 + external/badvpn_dns/structure/SAvl_impl.h | 164 + external/badvpn_dns/structure/SAvl_tree.h | 18 + external/badvpn_dns/structure/SLinkedList.h | 38 + .../badvpn_dns/structure/SLinkedList_decl.h | 67 + .../badvpn_dns/structure/SLinkedList_footer.h | 57 + .../badvpn_dns/structure/SLinkedList_header.h | 62 + .../badvpn_dns/structure/SLinkedList_impl.h | 182 + external/badvpn_dns/system/BAddr.h | 808 +++ external/badvpn_dns/system/BConnection.h | 369 ++ .../badvpn_dns/system/BConnectionGeneric.h | 144 + external/badvpn_dns/system/BConnection_unix.c | 1057 ++++ external/badvpn_dns/system/BConnection_unix.h | 87 + external/badvpn_dns/system/BConnection_win.c | 875 +++ external/badvpn_dns/system/BConnection_win.h | 101 + external/badvpn_dns/system/BDatagram.h | 209 + external/badvpn_dns/system/BDatagram_unix.c | 855 +++ external/badvpn_dns/system/BDatagram_unix.h | 71 + external/badvpn_dns/system/BDatagram_win.c | 755 +++ external/badvpn_dns/system/BDatagram_win.h | 99 + external/badvpn_dns/system/BInputProcess.c | 211 + external/badvpn_dns/system/BInputProcess.h | 65 + external/badvpn_dns/system/BLockReactor.c | 131 + external/badvpn_dns/system/BLockReactor.h | 58 + external/badvpn_dns/system/BNetwork.c | 99 + external/badvpn_dns/system/BNetwork.h | 36 + external/badvpn_dns/system/BProcess.c | 400 ++ external/badvpn_dns/system/BProcess.h | 200 + external/badvpn_dns/system/BReactor.h | 11 + external/badvpn_dns/system/BReactor_badvpn.c | 1430 +++++ external/badvpn_dns/system/BReactor_badvpn.h | 572 ++ .../system/BReactor_badvpn_timerstree.h | 13 + .../badvpn_dns/system/BReactor_emscripten.c | 176 + .../badvpn_dns/system/BReactor_emscripten.h | 87 + external/badvpn_dns/system/BReactor_glib.c | 524 ++ external/badvpn_dns/system/BReactor_glib.h | 148 + external/badvpn_dns/system/BSignal.c | 188 + external/badvpn_dns/system/BSignal.h | 64 + external/badvpn_dns/system/BThreadSignal.c | 136 + external/badvpn_dns/system/BThreadSignal.h | 53 + external/badvpn_dns/system/BTime.c | 38 + external/badvpn_dns/system/BTime.h | 163 + external/badvpn_dns/system/BUnixSignal.c | 406 ++ external/badvpn_dns/system/BUnixSignal.h | 132 + external/badvpn_dns/system/CMakeLists.txt | 44 + external/badvpn_dns/tests/CMakeLists.txt | 8 + external/badvpn_dns/tests/bproto_test.bproto | 9 + external/badvpn_dns/tests/bproto_test.c | 76 + external/badvpn_dns/tests/chunkbuffer2_test.c | 86 + external/badvpn_dns/tests/threadwork_test.c | 87 + external/badvpn_dns/threadwork/BThreadWork.c | 451 ++ external/badvpn_dns/threadwork/BThreadWork.h | 171 + external/badvpn_dns/threadwork/CMakeLists.txt | 6 + external/badvpn_dns/tun2socks/CMakeLists.txt | 15 + .../badvpn_dns/tun2socks/SocksUdpGwClient.c | 228 + .../badvpn_dns/tun2socks/SocksUdpGwClient.h | 64 + .../badvpn_dns/tun2socks/badvpn-tun2socks.8 | 126 + external/badvpn_dns/tun2socks/tun2socks.c | 2138 +++++++ external/badvpn_dns/tun2socks/tun2socks.h | 46 + external/badvpn_dns/tunctl/CMakeLists.txt | 6 + external/badvpn_dns/tunctl/tunctl.c | 352 ++ external/badvpn_dns/tuntap/BTap.c | 631 +++ external/badvpn_dns/tuntap/BTap.h | 199 + external/badvpn_dns/tuntap/CMakeLists.txt | 10 + external/badvpn_dns/tuntap/tapwin32-funcs.c | 227 + external/badvpn_dns/tuntap/tapwin32-funcs.h | 42 + external/badvpn_dns/tuntap/wintap-common.h | 39 + .../badvpn_dns/udevmonitor/CMakeLists.txt | 7 + .../badvpn_dns/udevmonitor/NCDUdevCache.c | 417 ++ .../badvpn_dns/udevmonitor/NCDUdevCache.h | 66 + .../badvpn_dns/udevmonitor/NCDUdevManager.c | 547 ++ .../badvpn_dns/udevmonitor/NCDUdevManager.h | 84 + .../badvpn_dns/udevmonitor/NCDUdevMonitor.c | 250 + .../badvpn_dns/udevmonitor/NCDUdevMonitor.h | 71 + .../udevmonitor/NCDUdevMonitorParser.c | 358 ++ .../udevmonitor/NCDUdevMonitorParser.h | 76 + external/badvpn_dns/udpgw/CMakeLists.txt | 9 + external/badvpn_dns/udpgw/udpgw.c | 1473 +++++ external/badvpn_dns/udpgw/udpgw.h | 52 + .../badvpn_dns/udpgw_client/CMakeLists.txt | 1 + .../badvpn_dns/udpgw_client/UdpGwClient.c | 597 ++ .../badvpn_dns/udpgw_client/UdpGwClient.h | 118 + jni/Android.mk | 2 +- 972 files changed, 227044 insertions(+), 1 deletion(-) create mode 100644 external/badvpn_dns/Android.mk create mode 100644 external/badvpn_dns/CMakeLists.txt create mode 100644 external/badvpn_dns/COPYING create mode 100644 external/badvpn_dns/ChangeLog create mode 100644 external/badvpn_dns/INSTALL create mode 100644 external/badvpn_dns/INSTALL-WINDOWS create mode 100644 external/badvpn_dns/arpprobe/BArpProbe.c create mode 100644 external/badvpn_dns/arpprobe/BArpProbe.h create mode 100644 external/badvpn_dns/arpprobe/CMakeLists.txt create mode 100644 external/badvpn_dns/badvpn.7 create mode 100644 external/badvpn_dns/base/BLog.c create mode 100644 external/badvpn_dns/base/BLog.h create mode 100644 external/badvpn_dns/base/BLog_syslog.c create mode 100644 external/badvpn_dns/base/BLog_syslog.h create mode 100644 external/badvpn_dns/base/BMutex.h create mode 100644 external/badvpn_dns/base/BPending.c create mode 100644 external/badvpn_dns/base/BPending.h create mode 100644 external/badvpn_dns/base/BPending_list.h create mode 100644 external/badvpn_dns/base/CMakeLists.txt create mode 100644 external/badvpn_dns/base/DebugObject.c create mode 100644 external/badvpn_dns/base/DebugObject.h create mode 100644 external/badvpn_dns/blog_channels.txt create mode 100644 external/badvpn_dns/blog_generator/blog.php create mode 100644 external/badvpn_dns/blog_generator/blog_functions.php create mode 100644 external/badvpn_dns/bproto/BProto.h create mode 100644 external/badvpn_dns/bproto_generator/ProtoParser.lime create mode 100644 external/badvpn_dns/bproto_generator/ProtoParser.php create mode 100644 external/badvpn_dns/bproto_generator/bproto.php create mode 100644 external/badvpn_dns/bproto_generator/bproto_functions.php create mode 100644 external/badvpn_dns/client/CMakeLists.txt create mode 100644 external/badvpn_dns/client/DPReceive.c create mode 100644 external/badvpn_dns/client/DPReceive.h create mode 100644 external/badvpn_dns/client/DPRelay.c create mode 100644 external/badvpn_dns/client/DPRelay.h create mode 100644 external/badvpn_dns/client/DataProto.c create mode 100644 external/badvpn_dns/client/DataProto.h create mode 100644 external/badvpn_dns/client/DataProtoKeepaliveSource.c create mode 100644 external/badvpn_dns/client/DataProtoKeepaliveSource.h create mode 100644 external/badvpn_dns/client/DatagramPeerIO.c create mode 100644 external/badvpn_dns/client/DatagramPeerIO.h create mode 100644 external/badvpn_dns/client/FragmentProtoAssembler.c create mode 100644 external/badvpn_dns/client/FragmentProtoAssembler.h create mode 100644 external/badvpn_dns/client/FragmentProtoAssembler_tree.h create mode 100644 external/badvpn_dns/client/FragmentProtoDisassembler.c create mode 100644 external/badvpn_dns/client/FragmentProtoDisassembler.h create mode 100644 external/badvpn_dns/client/FrameDecider.c create mode 100644 external/badvpn_dns/client/FrameDecider.h create mode 100644 external/badvpn_dns/client/FrameDecider_groups_tree.h create mode 100644 external/badvpn_dns/client/FrameDecider_macs_tree.h create mode 100644 external/badvpn_dns/client/FrameDecider_multicast_tree.h create mode 100644 external/badvpn_dns/client/PasswordListener.c create mode 100644 external/badvpn_dns/client/PasswordListener.h create mode 100644 external/badvpn_dns/client/PeerChat.c create mode 100644 external/badvpn_dns/client/PeerChat.h create mode 100644 external/badvpn_dns/client/SCOutmsgEncoder.c create mode 100644 external/badvpn_dns/client/SCOutmsgEncoder.h create mode 100644 external/badvpn_dns/client/SPProtoDecoder.c create mode 100644 external/badvpn_dns/client/SPProtoDecoder.h create mode 100644 external/badvpn_dns/client/SPProtoEncoder.c create mode 100644 external/badvpn_dns/client/SPProtoEncoder.h create mode 100644 external/badvpn_dns/client/SimpleStreamBuffer.c create mode 100644 external/badvpn_dns/client/SimpleStreamBuffer.h create mode 100644 external/badvpn_dns/client/SinglePacketSource.c create mode 100644 external/badvpn_dns/client/SinglePacketSource.h create mode 100644 external/badvpn_dns/client/StreamPeerIO.c create mode 100644 external/badvpn_dns/client/StreamPeerIO.h create mode 100644 external/badvpn_dns/client/badvpn-client.8 create mode 100644 external/badvpn_dns/client/client.c create mode 100644 external/badvpn_dns/client/client.h create mode 100644 external/badvpn_dns/cmake/modules/COPYING-CMAKE-SCRIPTS create mode 100644 external/badvpn_dns/cmake/modules/FindGLIB2.cmake create mode 100644 external/badvpn_dns/cmake/modules/FindLibraryWithDebug.cmake create mode 100644 external/badvpn_dns/cmake/modules/FindNSPR.cmake create mode 100644 external/badvpn_dns/cmake/modules/FindNSS.cmake create mode 100644 external/badvpn_dns/cmake/modules/FindOpenSSL.cmake create mode 100755 external/badvpn_dns/compile-tun2sock.sh create mode 100755 external/badvpn_dns/compile-udpgw.sh create mode 100644 external/badvpn_dns/dhcpclient/BDHCPClient.c create mode 100644 external/badvpn_dns/dhcpclient/BDHCPClient.h create mode 100644 external/badvpn_dns/dhcpclient/BDHCPClientCore.c create mode 100644 external/badvpn_dns/dhcpclient/BDHCPClientCore.h create mode 100644 external/badvpn_dns/dhcpclient/CMakeLists.txt create mode 100644 external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.c create mode 100644 external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.h create mode 100644 external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.c create mode 100644 external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.h create mode 100644 external/badvpn_dns/dostest/CMakeLists.txt create mode 100644 external/badvpn_dns/dostest/StreamBuffer.c create mode 100644 external/badvpn_dns/dostest/StreamBuffer.h create mode 100644 external/badvpn_dns/dostest/dostest-attacker.c create mode 100644 external/badvpn_dns/dostest/dostest-server.c create mode 100644 external/badvpn_dns/examples/CMakeLists.txt create mode 100644 external/badvpn_dns/examples/FastPacketSource.h create mode 100644 external/badvpn_dns/examples/RandomPacketSink.h create mode 100644 external/badvpn_dns/examples/TimerPacketSink.h create mode 100644 external/badvpn_dns/examples/arpprobe_test.c create mode 100644 external/badvpn_dns/examples/bavl_test.c create mode 100644 external/badvpn_dns/examples/bencryption_bench.c create mode 100644 external/badvpn_dns/examples/bprocess_example.c create mode 100644 external/badvpn_dns/examples/brandom2_test.c create mode 100644 external/badvpn_dns/examples/btimer_example.c create mode 100644 external/badvpn_dns/examples/cavl_test.c create mode 100644 external/badvpn_dns/examples/cavl_test_tree.h create mode 100644 external/badvpn_dns/examples/dhcpclient_test.c create mode 100644 external/badvpn_dns/examples/emscripten_test.c create mode 100644 external/badvpn_dns/examples/fairqueue_test.c create mode 100644 external/badvpn_dns/examples/fairqueue_test2.c create mode 100644 external/badvpn_dns/examples/indexedlist_test.c create mode 100644 external/badvpn_dns/examples/ipaddr6_test.c create mode 100644 external/badvpn_dns/examples/ncd_parser_test.c create mode 100644 external/badvpn_dns/examples/ncd_tokenizer_test.c create mode 100644 external/badvpn_dns/examples/ncd_value_parser_test.c create mode 100644 external/badvpn_dns/examples/ncdinterfacemonitor_test.c create mode 100644 external/badvpn_dns/examples/ncdudevmanager_test.c create mode 100644 external/badvpn_dns/examples/ncdudevmonitor_test.c create mode 100644 external/badvpn_dns/examples/ncdval_test.c create mode 100644 external/badvpn_dns/examples/ncdvalcons_test.c create mode 100644 external/badvpn_dns/examples/parse_number_test.c create mode 100644 external/badvpn_dns/examples/predicate_test.c create mode 100644 external/badvpn_dns/examples/savl_test.c create mode 100644 external/badvpn_dns/examples/savl_test_tree.h create mode 100644 external/badvpn_dns/examples/stdin_input.c create mode 100644 external/badvpn_dns/examples/substring_test.c create mode 100644 external/badvpn_dns/fix_flex.php create mode 100644 external/badvpn_dns/flooder/CMakeLists.txt create mode 100644 external/badvpn_dns/flooder/flooder.c create mode 100644 external/badvpn_dns/flooder/flooder.h create mode 100644 external/badvpn_dns/flow/BufferWriter.c create mode 100644 external/badvpn_dns/flow/BufferWriter.h create mode 100644 external/badvpn_dns/flow/CMakeLists.txt create mode 100644 external/badvpn_dns/flow/LineBuffer.c create mode 100644 external/badvpn_dns/flow/LineBuffer.h create mode 100644 external/badvpn_dns/flow/PacketBuffer.c create mode 100644 external/badvpn_dns/flow/PacketBuffer.h create mode 100644 external/badvpn_dns/flow/PacketCopier.c create mode 100644 external/badvpn_dns/flow/PacketCopier.h create mode 100644 external/badvpn_dns/flow/PacketPassConnector.c create mode 100644 external/badvpn_dns/flow/PacketPassConnector.h create mode 100644 external/badvpn_dns/flow/PacketPassFairQueue.c create mode 100644 external/badvpn_dns/flow/PacketPassFairQueue.h create mode 100644 external/badvpn_dns/flow/PacketPassFairQueue_tree.h create mode 100644 external/badvpn_dns/flow/PacketPassFifoQueue.c create mode 100644 external/badvpn_dns/flow/PacketPassFifoQueue.h create mode 100644 external/badvpn_dns/flow/PacketPassInterface.c create mode 100644 external/badvpn_dns/flow/PacketPassInterface.h create mode 100644 external/badvpn_dns/flow/PacketPassNotifier.c create mode 100644 external/badvpn_dns/flow/PacketPassNotifier.h create mode 100644 external/badvpn_dns/flow/PacketPassPriorityQueue.c create mode 100644 external/badvpn_dns/flow/PacketPassPriorityQueue.h create mode 100644 external/badvpn_dns/flow/PacketPassPriorityQueue_tree.h create mode 100644 external/badvpn_dns/flow/PacketProtoDecoder.c create mode 100644 external/badvpn_dns/flow/PacketProtoDecoder.h create mode 100644 external/badvpn_dns/flow/PacketProtoEncoder.c create mode 100644 external/badvpn_dns/flow/PacketProtoEncoder.h create mode 100644 external/badvpn_dns/flow/PacketProtoFlow.c create mode 100644 external/badvpn_dns/flow/PacketProtoFlow.h create mode 100644 external/badvpn_dns/flow/PacketRecvBlocker.c create mode 100644 external/badvpn_dns/flow/PacketRecvBlocker.h create mode 100644 external/badvpn_dns/flow/PacketRecvConnector.c create mode 100644 external/badvpn_dns/flow/PacketRecvConnector.h create mode 100644 external/badvpn_dns/flow/PacketRecvInterface.c create mode 100644 external/badvpn_dns/flow/PacketRecvInterface.h create mode 100644 external/badvpn_dns/flow/PacketRouter.c create mode 100644 external/badvpn_dns/flow/PacketRouter.h create mode 100644 external/badvpn_dns/flow/PacketStreamSender.c create mode 100644 external/badvpn_dns/flow/PacketStreamSender.h create mode 100644 external/badvpn_dns/flow/RouteBuffer.c create mode 100644 external/badvpn_dns/flow/RouteBuffer.h create mode 100644 external/badvpn_dns/flow/SinglePacketBuffer.c create mode 100644 external/badvpn_dns/flow/SinglePacketBuffer.h create mode 100644 external/badvpn_dns/flow/SinglePacketSender.c create mode 100644 external/badvpn_dns/flow/SinglePacketSender.h create mode 100644 external/badvpn_dns/flow/SingleStreamReceiver.c create mode 100644 external/badvpn_dns/flow/SingleStreamReceiver.h create mode 100644 external/badvpn_dns/flow/SingleStreamSender.c create mode 100644 external/badvpn_dns/flow/SingleStreamSender.h create mode 100644 external/badvpn_dns/flow/StreamPacketSender.c create mode 100644 external/badvpn_dns/flow/StreamPacketSender.h create mode 100644 external/badvpn_dns/flow/StreamPassConnector.c create mode 100644 external/badvpn_dns/flow/StreamPassConnector.h create mode 100644 external/badvpn_dns/flow/StreamPassInterface.c create mode 100644 external/badvpn_dns/flow/StreamPassInterface.h create mode 100644 external/badvpn_dns/flow/StreamRecvConnector.c create mode 100644 external/badvpn_dns/flow/StreamRecvConnector.h create mode 100644 external/badvpn_dns/flow/StreamRecvInterface.c create mode 100644 external/badvpn_dns/flow/StreamRecvInterface.h create mode 100644 external/badvpn_dns/flowextra/CMakeLists.txt create mode 100644 external/badvpn_dns/flowextra/KeepaliveIO.c create mode 100644 external/badvpn_dns/flowextra/KeepaliveIO.h create mode 100644 external/badvpn_dns/flowextra/PacketPassInactivityMonitor.c create mode 100644 external/badvpn_dns/flowextra/PacketPassInactivityMonitor.h create mode 100755 external/badvpn_dns/generate_files create mode 100644 external/badvpn_dns/generated/NCDConfigParser_parse.c create mode 100644 external/badvpn_dns/generated/NCDConfigParser_parse.h create mode 100644 external/badvpn_dns/generated/NCDConfigParser_parse.out create mode 100644 external/badvpn_dns/generated/NCDConfigParser_parse.y create mode 100644 external/badvpn_dns/generated/NCDValParser_parse.c create mode 100644 external/badvpn_dns/generated/NCDValParser_parse.h create mode 100644 external/badvpn_dns/generated/NCDValParser_parse.out create mode 100644 external/badvpn_dns/generated/NCDValParser_parse.y create mode 100644 external/badvpn_dns/generated/bison_BPredicate.c create mode 100644 external/badvpn_dns/generated/bison_BPredicate.h create mode 100644 external/badvpn_dns/generated/blog_channel_BArpProbe.h create mode 100644 external/badvpn_dns/generated/blog_channel_BConnection.h create mode 100644 external/badvpn_dns/generated/blog_channel_BDHCPClient.h create mode 100644 external/badvpn_dns/generated/blog_channel_BDHCPClientCore.h create mode 100644 external/badvpn_dns/generated/blog_channel_BDatagram.h create mode 100644 external/badvpn_dns/generated/blog_channel_BEncryption.h create mode 100644 external/badvpn_dns/generated/blog_channel_BInputProcess.h create mode 100644 external/badvpn_dns/generated/blog_channel_BLockReactor.h create mode 100644 external/badvpn_dns/generated/blog_channel_BNetwork.h create mode 100644 external/badvpn_dns/generated/blog_channel_BPredicate.h create mode 100644 external/badvpn_dns/generated/blog_channel_BProcess.h create mode 100644 external/badvpn_dns/generated/blog_channel_BReactor.h create mode 100644 external/badvpn_dns/generated/blog_channel_BSSLConnection.h create mode 100644 external/badvpn_dns/generated/blog_channel_BSignal.h create mode 100644 external/badvpn_dns/generated/blog_channel_BSocksClient.h create mode 100644 external/badvpn_dns/generated/blog_channel_BTap.h create mode 100644 external/badvpn_dns/generated/blog_channel_BThreadSignal.h create mode 100644 external/badvpn_dns/generated/blog_channel_BThreadWork.h create mode 100644 external/badvpn_dns/generated/blog_channel_BTime.h create mode 100644 external/badvpn_dns/generated/blog_channel_BUnixSignal.h create mode 100644 external/badvpn_dns/generated/blog_channel_DPReceive.h create mode 100644 external/badvpn_dns/generated/blog_channel_DPRelay.h create mode 100644 external/badvpn_dns/generated/blog_channel_DataProto.h create mode 100644 external/badvpn_dns/generated/blog_channel_DatagramPeerIO.h create mode 100644 external/badvpn_dns/generated/blog_channel_FragmentProtoAssembler.h create mode 100644 external/badvpn_dns/generated/blog_channel_FrameDecider.h create mode 100644 external/badvpn_dns/generated/blog_channel_LineBuffer.h create mode 100644 external/badvpn_dns/generated/blog_channel_Listener.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDBuildProgram.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDConfigParser.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDConfigTokenizer.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDIfConfig.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDInterfaceMonitor.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDModuleIndex.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDModuleProcess.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDPlaceholderDb.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDRequest.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDRequestClient.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDRfkillMonitor.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDUdevCache.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDUdevManager.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDUdevMonitor.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDUdevMonitorParser.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDVal.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDValGenerator.h create mode 100644 external/badvpn_dns/generated/blog_channel_NCDValParser.h create mode 100644 external/badvpn_dns/generated/blog_channel_PRStreamSink.h create mode 100644 external/badvpn_dns/generated/blog_channel_PRStreamSource.h create mode 100644 external/badvpn_dns/generated/blog_channel_PacketProtoDecoder.h create mode 100644 external/badvpn_dns/generated/blog_channel_PasswordListener.h create mode 100644 external/badvpn_dns/generated/blog_channel_PeerChat.h create mode 100644 external/badvpn_dns/generated/blog_channel_SPProtoDecoder.h create mode 100644 external/badvpn_dns/generated/blog_channel_ServerConnection.h create mode 100644 external/badvpn_dns/generated/blog_channel_SocksUdpGwClient.h create mode 100644 external/badvpn_dns/generated/blog_channel_StreamPeerIO.h create mode 100644 external/badvpn_dns/generated/blog_channel_UdpGwClient.h create mode 100644 external/badvpn_dns/generated/blog_channel_addr.h create mode 100644 external/badvpn_dns/generated/blog_channel_client.h create mode 100644 external/badvpn_dns/generated/blog_channel_dostest_attacker.h create mode 100644 external/badvpn_dns/generated/blog_channel_dostest_server.h create mode 100644 external/badvpn_dns/generated/blog_channel_flooder.h create mode 100644 external/badvpn_dns/generated/blog_channel_lwip.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_alias.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_arithmetic.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_assert.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_backtrack.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_blocker.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_buffer.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_call2.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_choose.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_concat.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_daemon.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_depend.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_depend_scope.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_dynamic_depend.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_exit.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_explode.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_file.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_file_open.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_foreach.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_from_string.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_getargs.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_getenv.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_if.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_imperative.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_implode.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_index.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_list.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_load_module.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_log.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_log_msg.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_logical.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_multidepend.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_backend_badvpn.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_backend_rfkill.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitdevice.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitlink.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_backend_wpa_supplicant.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_dns.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_iptables.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr_in_network.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_arp_probe.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_dhcp.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_route.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr_in_network.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_route.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_up.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_net_watch_interfaces.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_netmask.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_ondemand.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_parse.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_print.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_process_manager.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_reboot.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_ref.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_regex_match.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_request.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_run.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_runonce.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sleep.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_socket.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_spawn.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_strcmp.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_substr.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_evdev.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_request_client.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_request_server.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_start_process.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_watch_directory.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_watch_input.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_sys_watch_usb.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_timer.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_to_string.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_try.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_value.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_valuemetic.h create mode 100644 external/badvpn_dns/generated/blog_channel_ncd_var.h create mode 100644 external/badvpn_dns/generated/blog_channel_nsskey.h create mode 100644 external/badvpn_dns/generated/blog_channel_server.h create mode 100644 external/badvpn_dns/generated/blog_channel_tun2socks.h create mode 100644 external/badvpn_dns/generated/blog_channel_udpgw.h create mode 100644 external/badvpn_dns/generated/blog_channels_defines.h create mode 100644 external/badvpn_dns/generated/blog_channels_list.h create mode 100644 external/badvpn_dns/generated/bproto_addr.h create mode 100644 external/badvpn_dns/generated/bproto_bproto_test.h create mode 100644 external/badvpn_dns/generated/bproto_msgproto.h create mode 100644 external/badvpn_dns/generated/flex_BPredicate.c create mode 100644 external/badvpn_dns/generated/flex_BPredicate.h create mode 100644 external/badvpn_dns/lemon/lemon.c create mode 100644 external/badvpn_dns/lemon/lempar.c create mode 100644 external/badvpn_dns/lime/HOWTO create mode 100644 external/badvpn_dns/lime/flex_token_stream.php create mode 100644 external/badvpn_dns/lime/lemon.c create mode 100644 external/badvpn_dns/lime/lime.bootstrap create mode 100644 external/badvpn_dns/lime/lime.php create mode 100644 external/badvpn_dns/lime/lime_scan_tokens.l create mode 100644 external/badvpn_dns/lime/metagrammar create mode 100644 external/badvpn_dns/lime/parse_engine.php create mode 100644 external/badvpn_dns/lime/set.so.php create mode 100644 external/badvpn_dns/lwip/CHANGELOG create mode 100644 external/badvpn_dns/lwip/CMakeLists.txt create mode 100644 external/badvpn_dns/lwip/COPYING create mode 100644 external/badvpn_dns/lwip/FILES create mode 100644 external/badvpn_dns/lwip/README create mode 100644 external/badvpn_dns/lwip/UPGRADING create mode 100644 external/badvpn_dns/lwip/custom/arch/cc.h create mode 100644 external/badvpn_dns/lwip/custom/arch/perf.h create mode 100644 external/badvpn_dns/lwip/custom/lwipopts.h create mode 100644 external/badvpn_dns/lwip/custom/sys.c create mode 100644 external/badvpn_dns/lwip/doc/FILES create mode 100644 external/badvpn_dns/lwip/doc/contrib.txt create mode 100644 external/badvpn_dns/lwip/doc/rawapi.txt create mode 100644 external/badvpn_dns/lwip/doc/savannah.txt create mode 100644 external/badvpn_dns/lwip/doc/snmp_agent.txt create mode 100644 external/badvpn_dns/lwip/doc/sys_arch.txt create mode 100644 external/badvpn_dns/lwip/lwip-base-version create mode 100644 external/badvpn_dns/lwip/src/FILES create mode 100644 external/badvpn_dns/lwip/src/api/api_lib.c create mode 100644 external/badvpn_dns/lwip/src/api/api_msg.c create mode 100644 external/badvpn_dns/lwip/src/api/err.c create mode 100644 external/badvpn_dns/lwip/src/api/netbuf.c create mode 100644 external/badvpn_dns/lwip/src/api/netdb.c create mode 100644 external/badvpn_dns/lwip/src/api/netifapi.c create mode 100644 external/badvpn_dns/lwip/src/api/sockets.c create mode 100644 external/badvpn_dns/lwip/src/api/tcpip.c create mode 100644 external/badvpn_dns/lwip/src/core/def.c create mode 100644 external/badvpn_dns/lwip/src/core/dhcp.c create mode 100644 external/badvpn_dns/lwip/src/core/dns.c create mode 100644 external/badvpn_dns/lwip/src/core/inet_chksum.c create mode 100644 external/badvpn_dns/lwip/src/core/init.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/autoip.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/icmp.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/igmp.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/ip4.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/ip4_addr.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv4/ip_frag.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/README create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/dhcp6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/ethip6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/icmp6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/inet6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/ip6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/ip6_addr.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/ip6_frag.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/mld6.c create mode 100644 external/badvpn_dns/lwip/src/core/ipv6/nd6.c create mode 100644 external/badvpn_dns/lwip/src/core/mem.c create mode 100644 external/badvpn_dns/lwip/src/core/memp.c create mode 100644 external/badvpn_dns/lwip/src/core/netif.c create mode 100644 external/badvpn_dns/lwip/src/core/pbuf.c create mode 100644 external/badvpn_dns/lwip/src/core/raw.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/asn1_dec.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/asn1_enc.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/mib2.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/mib_structs.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/msg_in.c create mode 100644 external/badvpn_dns/lwip/src/core/snmp/msg_out.c create mode 100644 external/badvpn_dns/lwip/src/core/stats.c create mode 100644 external/badvpn_dns/lwip/src/core/sys.c create mode 100644 external/badvpn_dns/lwip/src/core/tcp.c create mode 100644 external/badvpn_dns/lwip/src/core/tcp_in.c create mode 100644 external/badvpn_dns/lwip/src/core/tcp_out.c create mode 100644 external/badvpn_dns/lwip/src/core/timers.c create mode 100644 external/badvpn_dns/lwip/src/core/udp.c create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/autoip.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/icmp.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/igmp.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/inet.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4_addr.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv4/lwip/ip_frag.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/dhcp6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/ethip6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/icmp6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/inet6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_addr.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_frag.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/mld6.h create mode 100644 external/badvpn_dns/lwip/src/include/ipv6/lwip/nd6.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/api.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/api_msg.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/arch.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/debug.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/def.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/dhcp.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/dns.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/err.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/inet_chksum.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/init.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/ip.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/ip_addr.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/mem.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/memp.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/memp_std.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/netbuf.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/netdb.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/netif.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/netifapi.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/opt.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/pbuf.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/raw.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/sio.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/snmp.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/snmp_asn1.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/snmp_msg.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/snmp_structs.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/sockets.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/stats.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/sys.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/tcp.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/tcp_impl.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/tcpip.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/timers.h create mode 100644 external/badvpn_dns/lwip/src/include/lwip/udp.h create mode 100644 external/badvpn_dns/lwip/src/include/netif/etharp.h create mode 100644 external/badvpn_dns/lwip/src/include/netif/ppp_oe.h create mode 100644 external/badvpn_dns/lwip/src/include/netif/slipif.h create mode 100644 external/badvpn_dns/lwip/src/include/posix/netdb.h create mode 100644 external/badvpn_dns/lwip/src/include/posix/sys/socket.h create mode 100644 external/badvpn_dns/lwip/src/netif/FILES create mode 100644 external/badvpn_dns/lwip/src/netif/etharp.c create mode 100644 external/badvpn_dns/lwip/src/netif/ethernetif.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/auth.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/auth.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/chap.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/chap.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/chpms.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/chpms.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/fsm.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/fsm.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ipcp.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ipcp.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/lcp.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/lcp.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/magic.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/magic.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/md5.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/md5.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/pap.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/pap.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ppp.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ppp.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ppp_impl.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/ppp_oe.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/pppdebug.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/randm.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/randm.h create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/readme.txt create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/vj.c create mode 100644 external/badvpn_dns/lwip/src/netif/ppp/vj.h create mode 100644 external/badvpn_dns/lwip/src/netif/slipif.c create mode 100644 external/badvpn_dns/lwip/test/unit/core/test_mem.c create mode 100644 external/badvpn_dns/lwip/test/unit/core/test_mem.h create mode 100644 external/badvpn_dns/lwip/test/unit/core/test_pbuf.c create mode 100644 external/badvpn_dns/lwip/test/unit/core/test_pbuf.h create mode 100644 external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.c create mode 100644 external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.h create mode 100644 external/badvpn_dns/lwip/test/unit/etharp/test_etharp.c create mode 100644 external/badvpn_dns/lwip/test/unit/etharp/test_etharp.h create mode 100644 external/badvpn_dns/lwip/test/unit/lwip_check.h create mode 100644 external/badvpn_dns/lwip/test/unit/lwip_unittests.c create mode 100644 external/badvpn_dns/lwip/test/unit/lwipopts.h create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.c create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.h create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/test_tcp.c create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/test_tcp.h create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.c create mode 100644 external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.h create mode 100644 external/badvpn_dns/lwip/test/unit/udp/test_udp.c create mode 100644 external/badvpn_dns/lwip/test/unit/udp/test_udp.h create mode 100644 external/badvpn_dns/misc/BRefTarget.h create mode 100644 external/badvpn_dns/misc/Utf16Decoder.h create mode 100644 external/badvpn_dns/misc/Utf16Encoder.h create mode 100644 external/badvpn_dns/misc/Utf8Decoder.h create mode 100644 external/badvpn_dns/misc/Utf8Encoder.h create mode 100644 external/badvpn_dns/misc/arp_proto.h create mode 100644 external/badvpn_dns/misc/array_length.h create mode 100644 external/badvpn_dns/misc/balign.h create mode 100644 external/badvpn_dns/misc/balloc.h create mode 100644 external/badvpn_dns/misc/blimits.h create mode 100644 external/badvpn_dns/misc/bsize.h create mode 100644 external/badvpn_dns/misc/bsort.h create mode 100644 external/badvpn_dns/misc/bstring.h create mode 100644 external/badvpn_dns/misc/byteorder.h create mode 100644 external/badvpn_dns/misc/cmdline.h create mode 100644 external/badvpn_dns/misc/compare.h create mode 100644 external/badvpn_dns/misc/concat_strings.h create mode 100644 external/badvpn_dns/misc/cstring.h create mode 100644 external/badvpn_dns/misc/dead.h create mode 100644 external/badvpn_dns/misc/debug.h create mode 100644 external/badvpn_dns/misc/debugcounter.h create mode 100644 external/badvpn_dns/misc/debugerror.h create mode 100644 external/badvpn_dns/misc/dhcp_proto.h create mode 100644 external/badvpn_dns/misc/ethernet_proto.h create mode 100644 external/badvpn_dns/misc/exparray.h create mode 100644 external/badvpn_dns/misc/expstring.h create mode 100644 external/badvpn_dns/misc/find_char.h create mode 100644 external/badvpn_dns/misc/find_program.h create mode 100644 external/badvpn_dns/misc/get_iface_info.h create mode 100644 external/badvpn_dns/misc/grow_array.h create mode 100644 external/badvpn_dns/misc/hashfun.h create mode 100644 external/badvpn_dns/misc/igmp_proto.h create mode 100644 external/badvpn_dns/misc/ipaddr.h create mode 100644 external/badvpn_dns/misc/ipaddr6.h create mode 100644 external/badvpn_dns/misc/ipv4_proto.h create mode 100644 external/badvpn_dns/misc/ipv6_proto.h create mode 100644 external/badvpn_dns/misc/loggers_string.h create mode 100644 external/badvpn_dns/misc/loglevel.h create mode 100644 external/badvpn_dns/misc/maxalign.h create mode 100644 external/badvpn_dns/misc/merge.h create mode 100644 external/badvpn_dns/misc/minmax.h create mode 100644 external/badvpn_dns/misc/modadd.h create mode 100644 external/badvpn_dns/misc/mswsock.h create mode 100644 external/badvpn_dns/misc/nonblocking.h create mode 100644 external/badvpn_dns/misc/nsskey.h create mode 100644 external/badvpn_dns/misc/offset.h create mode 100644 external/badvpn_dns/misc/open_standard_streams.h create mode 100644 external/badvpn_dns/misc/overflow.h create mode 100644 external/badvpn_dns/misc/packed.h create mode 100644 external/badvpn_dns/misc/parse_number.h create mode 100644 external/badvpn_dns/misc/print_macros.h create mode 100644 external/badvpn_dns/misc/read_file.h create mode 100644 external/badvpn_dns/misc/read_write_int.h create mode 100644 external/badvpn_dns/misc/socks_proto.h create mode 100644 external/badvpn_dns/misc/sslsocket.h create mode 100644 external/badvpn_dns/misc/stdbuf_cmdline.h create mode 100644 external/badvpn_dns/misc/strdup.h create mode 100644 external/badvpn_dns/misc/string_begins_with.h create mode 100644 external/badvpn_dns/misc/substring.h create mode 100644 external/badvpn_dns/misc/udp_proto.h create mode 100644 external/badvpn_dns/misc/unicode_funcs.h create mode 100644 external/badvpn_dns/misc/version.h create mode 100644 external/badvpn_dns/misc/write_file.h create mode 100644 external/badvpn_dns/ncd-request/CMakeLists.txt create mode 100644 external/badvpn_dns/ncd-request/ncd-request.c create mode 100644 external/badvpn_dns/ncd/CMakeLists.txt create mode 100644 external/badvpn_dns/ncd/NCDAst.c create mode 100644 external/badvpn_dns/ncd/NCDAst.h create mode 100644 external/badvpn_dns/ncd/NCDBuildProgram.c create mode 100644 external/badvpn_dns/ncd/NCDBuildProgram.h create mode 100644 external/badvpn_dns/ncd/NCDConfigParser.c create mode 100644 external/badvpn_dns/ncd/NCDConfigParser.h create mode 100644 external/badvpn_dns/ncd/NCDConfigParser_parse.y create mode 100644 external/badvpn_dns/ncd/NCDConfigTokenizer.c create mode 100644 external/badvpn_dns/ncd/NCDConfigTokenizer.h create mode 100644 external/badvpn_dns/ncd/NCDInterpProcess.c create mode 100644 external/badvpn_dns/ncd/NCDInterpProcess.h create mode 100644 external/badvpn_dns/ncd/NCDInterpProg.c create mode 100644 external/badvpn_dns/ncd/NCDInterpProg.h create mode 100644 external/badvpn_dns/ncd/NCDInterpProg_hash.h create mode 100644 external/badvpn_dns/ncd/NCDInterpreter.c create mode 100644 external/badvpn_dns/ncd/NCDInterpreter.h create mode 100644 external/badvpn_dns/ncd/NCDMethodIndex.c create mode 100644 external/badvpn_dns/ncd/NCDMethodIndex.h create mode 100644 external/badvpn_dns/ncd/NCDMethodIndex_hash.h create mode 100644 external/badvpn_dns/ncd/NCDModule.c create mode 100644 external/badvpn_dns/ncd/NCDModule.h create mode 100644 external/badvpn_dns/ncd/NCDModuleIndex.c create mode 100644 external/badvpn_dns/ncd/NCDModuleIndex.h create mode 100644 external/badvpn_dns/ncd/NCDModuleIndex_mhash.h create mode 100644 external/badvpn_dns/ncd/NCDObject.c create mode 100644 external/badvpn_dns/ncd/NCDObject.h create mode 100644 external/badvpn_dns/ncd/NCDPlaceholderDb.c create mode 100644 external/badvpn_dns/ncd/NCDPlaceholderDb.h create mode 100644 external/badvpn_dns/ncd/NCDStringIndex.c create mode 100644 external/badvpn_dns/ncd/NCDStringIndex.h create mode 100644 external/badvpn_dns/ncd/NCDStringIndex_hash.h create mode 100644 external/badvpn_dns/ncd/NCDSugar.c create mode 100644 external/badvpn_dns/ncd/NCDSugar.h create mode 100644 external/badvpn_dns/ncd/NCDVal.c create mode 100644 external/badvpn_dns/ncd/NCDVal.h create mode 100644 external/badvpn_dns/ncd/NCDValCons.c create mode 100644 external/badvpn_dns/ncd/NCDValCons.h create mode 100644 external/badvpn_dns/ncd/NCDValGenerator.c create mode 100644 external/badvpn_dns/ncd/NCDValGenerator.h create mode 100644 external/badvpn_dns/ncd/NCDValParser.c create mode 100644 external/badvpn_dns/ncd/NCDValParser.h create mode 100644 external/badvpn_dns/ncd/NCDValParser_parse.y create mode 100644 external/badvpn_dns/ncd/NCDVal_maptree.h create mode 100644 external/badvpn_dns/ncd/README create mode 100644 external/badvpn_dns/ncd/emncd.c create mode 100644 external/badvpn_dns/ncd/emncd.html create mode 100644 external/badvpn_dns/ncd/examples/dbus_start.ncd create mode 100644 external/badvpn_dns/ncd/examples/dhcpd.conf.template create mode 100644 external/badvpn_dns/ncd/examples/directory_updater.ncd create mode 100644 external/badvpn_dns/ncd/examples/events.ncd create mode 100644 external/badvpn_dns/ncd/examples/igmpproxy.conf.template create mode 100644 external/badvpn_dns/ncd/examples/make_dhcp_config.ncd create mode 100644 external/badvpn_dns/ncd/examples/make_igmpproxy_config.ncd create mode 100644 external/badvpn_dns/ncd/examples/network.ncd create mode 100644 external/badvpn_dns/ncd/examples/onoff_server.ncdi create mode 100644 external/badvpn_dns/ncd/examples/onoff_server_test.ncd create mode 100644 external/badvpn_dns/ncd/examples/router/README create mode 100644 external/badvpn_dns/ncd/examples/router/add-port-forwarding create mode 100644 external/badvpn_dns/ncd/examples/router/dhcp_server.ncdi create mode 100644 external/badvpn_dns/ncd/examples/router/list-port-forwardings create mode 100644 external/badvpn_dns/ncd/examples/router/ncd.conf create mode 100644 external/badvpn_dns/ncd/examples/router/network.ncdi create mode 100644 external/badvpn_dns/ncd/examples/router/network_control_server.ncdi create mode 100644 external/badvpn_dns/ncd/examples/router/port_forwarding.ncdi create mode 100644 external/badvpn_dns/ncd/examples/router/pppoe.ncdi create mode 100644 external/badvpn_dns/ncd/examples/router/remove-port-forwarding create mode 100644 external/badvpn_dns/ncd/examples/router/unbound.ncdi create mode 100644 external/badvpn_dns/ncd/examples/tcp_echo_client.ncd create mode 100644 external/badvpn_dns/ncd/examples/tcp_echo_server.ncd create mode 100644 external/badvpn_dns/ncd/extra/BEventLock.c create mode 100644 external/badvpn_dns/ncd/extra/BEventLock.h create mode 100644 external/badvpn_dns/ncd/extra/NCDBProcessOpts.c create mode 100644 external/badvpn_dns/ncd/extra/NCDBProcessOpts.h create mode 100644 external/badvpn_dns/ncd/extra/NCDBuf.c create mode 100644 external/badvpn_dns/ncd/extra/NCDBuf.h create mode 100644 external/badvpn_dns/ncd/extra/NCDIfConfig.c create mode 100644 external/badvpn_dns/ncd/extra/NCDIfConfig.h create mode 100644 external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.c create mode 100644 external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.h create mode 100644 external/badvpn_dns/ncd/extra/NCDRequestClient.c create mode 100644 external/badvpn_dns/ncd/extra/NCDRequestClient.h create mode 100644 external/badvpn_dns/ncd/extra/NCDRfkillMonitor.c create mode 100644 external/badvpn_dns/ncd/extra/NCDRfkillMonitor.h create mode 100644 external/badvpn_dns/ncd/extra/address_utils.h create mode 100644 external/badvpn_dns/ncd/extra/build_cmdline.c create mode 100644 external/badvpn_dns/ncd/extra/build_cmdline.h create mode 100644 external/badvpn_dns/ncd/extra/make_fast_names.h create mode 100644 external/badvpn_dns/ncd/extra/value_utils.h create mode 100644 external/badvpn_dns/ncd/include_linux_input.c create mode 100644 external/badvpn_dns/ncd/make_name_indices.h create mode 100644 external/badvpn_dns/ncd/modules/alias.c create mode 100644 external/badvpn_dns/ncd/modules/arithmetic.c create mode 100644 external/badvpn_dns/ncd/modules/assert.c create mode 100644 external/badvpn_dns/ncd/modules/backtrack.c create mode 100644 external/badvpn_dns/ncd/modules/blocker.c create mode 100644 external/badvpn_dns/ncd/modules/buffer.c create mode 100644 external/badvpn_dns/ncd/modules/buffer_chunks_tree.h create mode 100644 external/badvpn_dns/ncd/modules/call2.c create mode 100644 external/badvpn_dns/ncd/modules/choose.c create mode 100644 external/badvpn_dns/ncd/modules/command_template.c create mode 100644 external/badvpn_dns/ncd/modules/command_template.h create mode 100644 external/badvpn_dns/ncd/modules/concat.c create mode 100644 external/badvpn_dns/ncd/modules/daemon.c create mode 100644 external/badvpn_dns/ncd/modules/depend.c create mode 100644 external/badvpn_dns/ncd/modules/depend_scope.c create mode 100644 external/badvpn_dns/ncd/modules/dynamic_depend.c create mode 100644 external/badvpn_dns/ncd/modules/event_template.c create mode 100644 external/badvpn_dns/ncd/modules/event_template.h create mode 100644 external/badvpn_dns/ncd/modules/exit.c create mode 100644 external/badvpn_dns/ncd/modules/explode.c create mode 100644 external/badvpn_dns/ncd/modules/file.c create mode 100644 external/badvpn_dns/ncd/modules/file_open.c create mode 100644 external/badvpn_dns/ncd/modules/foreach.c create mode 100644 external/badvpn_dns/ncd/modules/from_string.c create mode 100644 external/badvpn_dns/ncd/modules/getargs.c create mode 100644 external/badvpn_dns/ncd/modules/getenv.c create mode 100644 external/badvpn_dns/ncd/modules/if.c create mode 100644 external/badvpn_dns/ncd/modules/imperative.c create mode 100644 external/badvpn_dns/ncd/modules/implode.c create mode 100644 external/badvpn_dns/ncd/modules/index.c create mode 100644 external/badvpn_dns/ncd/modules/list.c create mode 100644 external/badvpn_dns/ncd/modules/load_module.c create mode 100644 external/badvpn_dns/ncd/modules/log.c create mode 100644 external/badvpn_dns/ncd/modules/logical.c create mode 100644 external/badvpn_dns/ncd/modules/modules.h create mode 100644 external/badvpn_dns/ncd/modules/multidepend.c create mode 100644 external/badvpn_dns/ncd/modules/net_backend_badvpn.c create mode 100644 external/badvpn_dns/ncd/modules/net_backend_rfkill.c create mode 100644 external/badvpn_dns/ncd/modules/net_backend_waitdevice.c create mode 100644 external/badvpn_dns/ncd/modules/net_backend_waitlink.c create mode 100644 external/badvpn_dns/ncd/modules/net_backend_wpa_supplicant.c create mode 100644 external/badvpn_dns/ncd/modules/net_dns.c create mode 100644 external/badvpn_dns/ncd/modules/net_iptables.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv4_addr.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv4_addr_in_network.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv4_arp_probe.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv4_dhcp.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv4_route.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv6_addr.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv6_addr_in_network.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv6_route.c create mode 100644 external/badvpn_dns/ncd/modules/net_ipv6_wait_dynamic_addr.c create mode 100644 external/badvpn_dns/ncd/modules/net_up.c create mode 100644 external/badvpn_dns/ncd/modules/net_watch_interfaces.c create mode 100644 external/badvpn_dns/ncd/modules/netmask.c create mode 100644 external/badvpn_dns/ncd/modules/ondemand.c create mode 100644 external/badvpn_dns/ncd/modules/parse.c create mode 100644 external/badvpn_dns/ncd/modules/print.c create mode 100644 external/badvpn_dns/ncd/modules/process_manager.c create mode 100644 external/badvpn_dns/ncd/modules/reboot.c create mode 100644 external/badvpn_dns/ncd/modules/ref.c create mode 100644 external/badvpn_dns/ncd/modules/regex_match.c create mode 100644 external/badvpn_dns/ncd/modules/run.c create mode 100644 external/badvpn_dns/ncd/modules/runonce.c create mode 100644 external/badvpn_dns/ncd/modules/sleep.c create mode 100644 external/badvpn_dns/ncd/modules/socket.c create mode 100644 external/badvpn_dns/ncd/modules/spawn.c create mode 100644 external/badvpn_dns/ncd/modules/strcmp.c create mode 100644 external/badvpn_dns/ncd/modules/substr.c create mode 100644 external/badvpn_dns/ncd/modules/sys_evdev.c create mode 100644 external/badvpn_dns/ncd/modules/sys_request_client.c create mode 100644 external/badvpn_dns/ncd/modules/sys_request_server.c create mode 100644 external/badvpn_dns/ncd/modules/sys_start_process.c create mode 100644 external/badvpn_dns/ncd/modules/sys_watch_directory.c create mode 100644 external/badvpn_dns/ncd/modules/sys_watch_input.c create mode 100644 external/badvpn_dns/ncd/modules/sys_watch_usb.c create mode 100644 external/badvpn_dns/ncd/modules/timer.c create mode 100644 external/badvpn_dns/ncd/modules/to_string.c create mode 100644 external/badvpn_dns/ncd/modules/try.c create mode 100644 external/badvpn_dns/ncd/modules/value.c create mode 100644 external/badvpn_dns/ncd/modules/value_maptree.h create mode 100644 external/badvpn_dns/ncd/modules/valuemetic.c create mode 100644 external/badvpn_dns/ncd/modules/var.c create mode 100644 external/badvpn_dns/ncd/ncd.c create mode 100644 external/badvpn_dns/ncd/ncd.h create mode 100755 external/badvpn_dns/ncd/parse_linux_input.sh create mode 100644 external/badvpn_dns/ncd/static_strings.h create mode 100644 external/badvpn_dns/ncd/tests/addr_in_network.ncd create mode 100644 external/badvpn_dns/ncd/tests/alias.ncd create mode 100644 external/badvpn_dns/ncd/tests/arithmetic.ncd create mode 100644 external/badvpn_dns/ncd/tests/backtracking.ncd create mode 100644 external/badvpn_dns/ncd/tests/buffer.ncd create mode 100644 external/badvpn_dns/ncd/tests/call.ncd create mode 100644 external/badvpn_dns/ncd/tests/concat.ncd create mode 100644 external/badvpn_dns/ncd/tests/depend.ncd create mode 100644 external/badvpn_dns/ncd/tests/depend_scope.ncd create mode 100644 external/badvpn_dns/ncd/tests/escape_and_nulls.ncd create mode 100644 external/badvpn_dns/ncd/tests/explode.ncd create mode 100644 external/badvpn_dns/ncd/tests/foreach.ncd create mode 100644 external/badvpn_dns/ncd/tests/if.ncd create mode 100644 external/badvpn_dns/ncd/tests/implode.ncd create mode 100644 external/badvpn_dns/ncd/tests/include.ncd create mode 100644 external/badvpn_dns/ncd/tests/include_included.ncdi create mode 100644 external/badvpn_dns/ncd/tests/include_included2.ncdi create mode 100644 external/badvpn_dns/ncd/tests/logical.ncd create mode 100644 external/badvpn_dns/ncd/tests/multidepend.ncd create mode 100644 external/badvpn_dns/ncd/tests/netmask.ncd create mode 100644 external/badvpn_dns/ncd/tests/parse.ncd create mode 100644 external/badvpn_dns/ncd/tests/process_manager.ncd create mode 100644 external/badvpn_dns/ncd/tests/regex.ncd create mode 100755 external/badvpn_dns/ncd/tests/run_tests create mode 100644 external/badvpn_dns/ncd/tests/strings.ncd create mode 100644 external/badvpn_dns/ncd/tests/substr.ncd create mode 100644 external/badvpn_dns/ncd/tests/turing.ncd create mode 100644 external/badvpn_dns/ncd/tests/value.ncd create mode 100644 external/badvpn_dns/ncd/tests/value_substr.ncd create mode 100644 external/badvpn_dns/nspr_support/BSSLConnection.c create mode 100644 external/badvpn_dns/nspr_support/BSSLConnection.h create mode 100644 external/badvpn_dns/nspr_support/CMakeLists.txt create mode 100644 external/badvpn_dns/nspr_support/DummyPRFileDesc.c create mode 100644 external/badvpn_dns/nspr_support/DummyPRFileDesc.h create mode 100644 external/badvpn_dns/predicate/BPredicate.c create mode 100644 external/badvpn_dns/predicate/BPredicate.h create mode 100644 external/badvpn_dns/predicate/BPredicate.l create mode 100644 external/badvpn_dns/predicate/BPredicate.y create mode 100644 external/badvpn_dns/predicate/BPredicate_internal.h create mode 100644 external/badvpn_dns/predicate/BPredicate_parser.h create mode 100644 external/badvpn_dns/predicate/CMakeLists.txt create mode 100644 external/badvpn_dns/predicate/LexMemoryBufferInput.h create mode 100644 external/badvpn_dns/protocol/addr.bproto create mode 100644 external/badvpn_dns/protocol/addr.h create mode 100644 external/badvpn_dns/protocol/dataproto.h create mode 100644 external/badvpn_dns/protocol/fragmentproto.h create mode 100644 external/badvpn_dns/protocol/msgproto.bproto create mode 100644 external/badvpn_dns/protocol/msgproto.h create mode 100644 external/badvpn_dns/protocol/packetproto.h create mode 100644 external/badvpn_dns/protocol/requestproto.h create mode 100644 external/badvpn_dns/protocol/scproto.h create mode 100644 external/badvpn_dns/protocol/spproto.h create mode 100644 external/badvpn_dns/protocol/udpgw_proto.h create mode 100644 external/badvpn_dns/random/BRandom2.c create mode 100644 external/badvpn_dns/random/BRandom2.h create mode 100644 external/badvpn_dns/random/CMakeLists.txt create mode 100755 external/badvpn_dns/scripts/cmake create mode 100755 external/badvpn_dns/scripts/copy_nss create mode 100644 external/badvpn_dns/scripts/toolchain.cmake create mode 100644 external/badvpn_dns/security/BEncryption.c create mode 100644 external/badvpn_dns/security/BEncryption.h create mode 100644 external/badvpn_dns/security/BHash.c create mode 100644 external/badvpn_dns/security/BHash.h create mode 100644 external/badvpn_dns/security/BRandom.c create mode 100644 external/badvpn_dns/security/BRandom.h create mode 100644 external/badvpn_dns/security/BSecurity.c create mode 100644 external/badvpn_dns/security/BSecurity.h create mode 100644 external/badvpn_dns/security/CMakeLists.txt create mode 100644 external/badvpn_dns/security/OTPCalculator.c create mode 100644 external/badvpn_dns/security/OTPCalculator.h create mode 100644 external/badvpn_dns/security/OTPChecker.c create mode 100644 external/badvpn_dns/security/OTPChecker.h create mode 100644 external/badvpn_dns/security/OTPGenerator.c create mode 100644 external/badvpn_dns/security/OTPGenerator.h create mode 100644 external/badvpn_dns/server/CMakeLists.txt create mode 100644 external/badvpn_dns/server/badvpn-server.8 create mode 100644 external/badvpn_dns/server/server.c create mode 100644 external/badvpn_dns/server/server.h create mode 100644 external/badvpn_dns/server_connection/CMakeLists.txt create mode 100644 external/badvpn_dns/server_connection/SCKeepaliveSource.c create mode 100644 external/badvpn_dns/server_connection/SCKeepaliveSource.h create mode 100644 external/badvpn_dns/server_connection/ServerConnection.c create mode 100644 external/badvpn_dns/server_connection/ServerConnection.h create mode 100644 external/badvpn_dns/socksclient/BSocksClient.c create mode 100644 external/badvpn_dns/socksclient/BSocksClient.h create mode 100644 external/badvpn_dns/socksclient/CMakeLists.txt create mode 100644 external/badvpn_dns/stringmap/BStringMap.c create mode 100644 external/badvpn_dns/stringmap/BStringMap.h create mode 100644 external/badvpn_dns/stringmap/CMakeLists.txt create mode 100644 external/badvpn_dns/structure/BAVL.h create mode 100644 external/badvpn_dns/structure/CAvl.h create mode 100644 external/badvpn_dns/structure/CAvl_decl.h create mode 100644 external/badvpn_dns/structure/CAvl_footer.h create mode 100644 external/badvpn_dns/structure/CAvl_header.h create mode 100644 external/badvpn_dns/structure/CAvl_impl.h create mode 100644 external/badvpn_dns/structure/CHash.h create mode 100644 external/badvpn_dns/structure/CHash_decl.h create mode 100644 external/badvpn_dns/structure/CHash_footer.h create mode 100644 external/badvpn_dns/structure/CHash_header.h create mode 100644 external/badvpn_dns/structure/CHash_impl.h create mode 100644 external/badvpn_dns/structure/ChunkBuffer2.h create mode 100644 external/badvpn_dns/structure/IndexedList.h create mode 100644 external/badvpn_dns/structure/IndexedList_tree.h create mode 100644 external/badvpn_dns/structure/LinkedList0.h create mode 100644 external/badvpn_dns/structure/LinkedList1.h create mode 100644 external/badvpn_dns/structure/LinkedList3.h create mode 100644 external/badvpn_dns/structure/SAvl.h create mode 100644 external/badvpn_dns/structure/SAvl_decl.h create mode 100644 external/badvpn_dns/structure/SAvl_footer.h create mode 100644 external/badvpn_dns/structure/SAvl_header.h create mode 100644 external/badvpn_dns/structure/SAvl_impl.h create mode 100644 external/badvpn_dns/structure/SAvl_tree.h create mode 100644 external/badvpn_dns/structure/SLinkedList.h create mode 100644 external/badvpn_dns/structure/SLinkedList_decl.h create mode 100644 external/badvpn_dns/structure/SLinkedList_footer.h create mode 100644 external/badvpn_dns/structure/SLinkedList_header.h create mode 100644 external/badvpn_dns/structure/SLinkedList_impl.h create mode 100644 external/badvpn_dns/system/BAddr.h create mode 100644 external/badvpn_dns/system/BConnection.h create mode 100644 external/badvpn_dns/system/BConnectionGeneric.h create mode 100644 external/badvpn_dns/system/BConnection_unix.c create mode 100644 external/badvpn_dns/system/BConnection_unix.h create mode 100644 external/badvpn_dns/system/BConnection_win.c create mode 100644 external/badvpn_dns/system/BConnection_win.h create mode 100644 external/badvpn_dns/system/BDatagram.h create mode 100644 external/badvpn_dns/system/BDatagram_unix.c create mode 100644 external/badvpn_dns/system/BDatagram_unix.h create mode 100644 external/badvpn_dns/system/BDatagram_win.c create mode 100644 external/badvpn_dns/system/BDatagram_win.h create mode 100644 external/badvpn_dns/system/BInputProcess.c create mode 100644 external/badvpn_dns/system/BInputProcess.h create mode 100644 external/badvpn_dns/system/BLockReactor.c create mode 100644 external/badvpn_dns/system/BLockReactor.h create mode 100644 external/badvpn_dns/system/BNetwork.c create mode 100644 external/badvpn_dns/system/BNetwork.h create mode 100644 external/badvpn_dns/system/BProcess.c create mode 100644 external/badvpn_dns/system/BProcess.h create mode 100644 external/badvpn_dns/system/BReactor.h create mode 100644 external/badvpn_dns/system/BReactor_badvpn.c create mode 100644 external/badvpn_dns/system/BReactor_badvpn.h create mode 100644 external/badvpn_dns/system/BReactor_badvpn_timerstree.h create mode 100644 external/badvpn_dns/system/BReactor_emscripten.c create mode 100644 external/badvpn_dns/system/BReactor_emscripten.h create mode 100644 external/badvpn_dns/system/BReactor_glib.c create mode 100644 external/badvpn_dns/system/BReactor_glib.h create mode 100644 external/badvpn_dns/system/BSignal.c create mode 100644 external/badvpn_dns/system/BSignal.h create mode 100644 external/badvpn_dns/system/BThreadSignal.c create mode 100644 external/badvpn_dns/system/BThreadSignal.h create mode 100644 external/badvpn_dns/system/BTime.c create mode 100644 external/badvpn_dns/system/BTime.h create mode 100644 external/badvpn_dns/system/BUnixSignal.c create mode 100644 external/badvpn_dns/system/BUnixSignal.h create mode 100644 external/badvpn_dns/system/CMakeLists.txt create mode 100644 external/badvpn_dns/tests/CMakeLists.txt create mode 100644 external/badvpn_dns/tests/bproto_test.bproto create mode 100644 external/badvpn_dns/tests/bproto_test.c create mode 100644 external/badvpn_dns/tests/chunkbuffer2_test.c create mode 100644 external/badvpn_dns/tests/threadwork_test.c create mode 100644 external/badvpn_dns/threadwork/BThreadWork.c create mode 100644 external/badvpn_dns/threadwork/BThreadWork.h create mode 100644 external/badvpn_dns/threadwork/CMakeLists.txt create mode 100644 external/badvpn_dns/tun2socks/CMakeLists.txt create mode 100644 external/badvpn_dns/tun2socks/SocksUdpGwClient.c create mode 100644 external/badvpn_dns/tun2socks/SocksUdpGwClient.h create mode 100644 external/badvpn_dns/tun2socks/badvpn-tun2socks.8 create mode 100644 external/badvpn_dns/tun2socks/tun2socks.c create mode 100644 external/badvpn_dns/tun2socks/tun2socks.h create mode 100644 external/badvpn_dns/tunctl/CMakeLists.txt create mode 100644 external/badvpn_dns/tunctl/tunctl.c create mode 100644 external/badvpn_dns/tuntap/BTap.c create mode 100644 external/badvpn_dns/tuntap/BTap.h create mode 100644 external/badvpn_dns/tuntap/CMakeLists.txt create mode 100644 external/badvpn_dns/tuntap/tapwin32-funcs.c create mode 100644 external/badvpn_dns/tuntap/tapwin32-funcs.h create mode 100644 external/badvpn_dns/tuntap/wintap-common.h create mode 100644 external/badvpn_dns/udevmonitor/CMakeLists.txt create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevCache.c create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevCache.h create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevManager.c create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevManager.h create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevMonitor.c create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevMonitor.h create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.c create mode 100644 external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.h create mode 100644 external/badvpn_dns/udpgw/CMakeLists.txt create mode 100644 external/badvpn_dns/udpgw/udpgw.c create mode 100644 external/badvpn_dns/udpgw/udpgw.h create mode 100644 external/badvpn_dns/udpgw_client/CMakeLists.txt create mode 100644 external/badvpn_dns/udpgw_client/UdpGwClient.c create mode 100644 external/badvpn_dns/udpgw_client/UdpGwClient.h diff --git a/external/badvpn_dns/Android.mk b/external/badvpn_dns/Android.mk new file mode 100644 index 00000000..38290654 --- /dev/null +++ b/external/badvpn_dns/Android.mk @@ -0,0 +1,75 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := tun2socks + +LOCAL_CFLAGS := -std=gnu99 +LOCAL_CFLAGS += -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE +LOCAL_CFLAGS += -DBADVPN_USE_SELFPIPE -DBADVPN_USE_EPOLL +LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN +LOCAL_CFLAGS += -DPSIPHON + +LOCAL_C_INCLUDES:= \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/lwip/src/include/ipv4 \ + $(LOCAL_PATH)/lwip/src/include/ipv6 \ + $(LOCAL_PATH)/lwip/src/include \ + $(LOCAL_PATH)/lwip/custom + +LOCAL_SRC_FILES := \ + base/BLog_syslog.c \ + system/BReactor_badvpn.c \ + system/BSignal.c \ + system/BConnection_unix.c \ + system/BTime.c \ + system/BUnixSignal.c \ + system/BNetwork.c \ + flow/StreamRecvInterface.c \ + flow/PacketRecvInterface.c \ + flow/PacketPassInterface.c \ + flow/StreamPassInterface.c \ + flow/SinglePacketBuffer.c \ + flow/BufferWriter.c \ + flow/PacketBuffer.c \ + flow/PacketStreamSender.c \ + flow/PacketPassConnector.c \ + flow/PacketProtoFlow.c \ + flow/PacketPassFairQueue.c \ + flow/PacketProtoEncoder.c \ + flow/PacketProtoDecoder.c \ + socksclient/BSocksClient.c \ + tuntap/BTap.c \ + lwip/src/core/timers.c \ + lwip/src/core/udp.c \ + lwip/src/core/memp.c \ + lwip/src/core/init.c \ + lwip/src/core/pbuf.c \ + lwip/src/core/tcp.c \ + lwip/src/core/tcp_out.c \ + lwip/src/core/netif.c \ + lwip/src/core/def.c \ + lwip/src/core/mem.c \ + lwip/src/core/tcp_in.c \ + lwip/src/core/stats.c \ + lwip/src/core/inet_chksum.c \ + lwip/src/core/ipv4/icmp.c \ + lwip/src/core/ipv4/ip4.c \ + lwip/src/core/ipv4/ip4_addr.c \ + lwip/src/core/ipv4/ip_frag.c \ + lwip/src/core/ipv6/ip6.c \ + lwip/src/core/ipv6/nd6.c \ + lwip/src/core/ipv6/icmp6.c \ + lwip/src/core/ipv6/ip6_addr.c \ + lwip/src/core/ipv6/ip6_frag.c \ + lwip/custom/sys.c \ + tun2socks/tun2socks.c \ + base/DebugObject.c \ + base/BLog.c \ + base/BPending.c \ + flowextra/PacketPassInactivityMonitor.c \ + tun2socks/SocksUdpGwClient.c \ + udpgw_client/UdpGwClient.c + +include $(BUILD_SHARED_LIBRARY) + diff --git a/external/badvpn_dns/CMakeLists.txt b/external/badvpn_dns/CMakeLists.txt new file mode 100644 index 00000000..ebdc0d7f --- /dev/null +++ b/external/badvpn_dns/CMakeLists.txt @@ -0,0 +1,408 @@ +cmake_minimum_required(VERSION 2.8) +project(BADVPN C) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") + +include(TestBigEndian) +include(CheckIncludeFiles) +include(CheckSymbolExists) +include(CheckTypeSize) + +set(BUILD_COMPONENTS) + +macro (build_switch name text default) + if (BUILD_NOTHING_BY_DEFAULT) + option(BUILD_${name} "${text}" OFF) + else () + option(BUILD_${name} "${text}" "${default}") + endif () + list(APPEND BUILD_COMPONENTS "${name}") +endmacro () + +# detect Emscripten +if (CMAKE_C_COMPILER MATCHES "/emcc$") + set(EMSCRIPTEN ON) +else () + set(EMSCRIPTEN OFF) +endif () + +if (EMSCRIPTEN) + set(ON_IF_NOT_EMSCRIPTEN OFF) +else () + set(ON_IF_NOT_EMSCRIPTEN ON) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT EMSCRIPTEN) + set(ON_IF_LINUX ON) +else () + set(ON_IF_LINUX OFF) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR EMSCRIPTEN) + set(ON_IF_LINUX_OR_EMSCRIPTEN ON) +else () + set(ON_IF_LINUX_OR_EMSCRIPTEN OFF) +endif () + +# define build defaults +build_switch(EXAMPLES "build example programs" ON) +build_switch(TESTS "build some other example programs" ON) +build_switch(SERVER "build badvpn-server" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(CLIENT "build badvpn-client" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(FLOODER "build badvpn-flooder" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(TUN2SOCKS "build badvpn-tun2socks" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(UDPGW "build badvpn-udpgw" ${ON_IF_NOT_EMSCRIPTEN}) +build_switch(NCD "build badvpn-ncd" ${ON_IF_LINUX_OR_EMSCRIPTEN}) +build_switch(TUNCTL "build badvpn-tunctl" ${ON_IF_LINUX}) +build_switch(DOSTEST "build dostest-server and dostest-attacker" OFF) + +if (BUILD_NCD AND NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "NCD is only available on Linux") +endif () + +if (BUILD_CLIENT OR BUILD_SERVER) + find_package(OpenSSL REQUIRED) + set(LIBCRYPTO_INCLUDE_DIRS "${OpenSSL_INCLUDE_DIRS}") + set(LIBCRYPTO_LIBRARY_DIRS "${OpenSSL_LIBRARY_DIRS}") + set(LIBCRYPTO_LIBRARIES "${OpenSSL_LIBRARIES}") +endif () + +if (BUILD_SERVER OR BUILD_CLIENT OR BUILD_FLOODER) + find_package(NSPR REQUIRED) + find_package(NSS REQUIRED) +endif () + +# choose reactor +if (DEFINED BREACTOR_BACKEND) + if (NOT (BREACTOR_BACKEND STREQUAL "badvpn" OR BREACTOR_BACKEND STREQUAL "glib")) + message(FATAL_ERROR "unknown reactor backend specified") + endif () +else () + if (EMSCRIPTEN) + set(BREACTOR_BACKEND "emscripten") + else () + set(BREACTOR_BACKEND "badvpn") + endif () +endif () + +if (BREACTOR_BACKEND STREQUAL "badvpn") + add_definitions(-DBADVPN_BREACTOR_BADVPN) +elseif (BREACTOR_BACKEND STREQUAL "glib") + if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "GLib reactor backend is only available on Linux") + endif () + find_package(GLIB2 REQUIRED) + add_definitions(-DBADVPN_BREACTOR_GLIB) +elseif (BREACTOR_BACKEND STREQUAL "emscripten") + add_definitions(-DBADVPN_BREACTOR_EMSCRIPTEN) +endif () + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBCRYPTO_INCLUDE_DIRS} + ${NSPR_INCLUDE_DIRS} + ${NSS_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIR} + lwip/custom + lwip/src/include + lwip/src/include/ipv4 + lwip/src/include/ipv6 +) + +link_directories( + ${LIBCRYPTO_LIBRARY_DIRS} + ${NSPR_LIBRARY_DIRS} + ${NSS_LIBRARY_DIRS} +) + +test_big_endian(BIG_ENDIAN) + +check_type_size(int INT_SIZE) +if (NOT (INT_SIZE GREATER "3")) + message(FATAL_ERROR "int must be at least 32 bits") +endif () + +check_type_size(size_t SIZE_SIZE) +if (NOT (SIZE_SIZE GREATER INT_SIZE OR SIZE_SIZE EQUAL INT_SIZE)) + message(FATAL_ERROR "size_t must be greater or equal than int") +endif () + +if (MSVC) + add_definitions(/TP -D_CRT_SECURE_NO_WARNINGS /wd4065 /wd4018 /wd4533 /wd4244 /wd4102) +else () + add_definitions(-std=gnu99 -Wall -Wno-unused-value -Wno-parentheses -Wno-switch -Wredundant-decls) + + if (NOT CMAKE_C_COMPILER_ID STREQUAL "PathScale") + add_definitions(-Werror=implicit-function-declaration -Wno-switch-enum -Wno-unused-function + -Wstrict-aliasing) + endif () + + if (CMAKE_C_COMPILER_ID MATCHES "^Clang") + add_definitions(-Wno-initializer-overrides -Wno-tautological-constant-out-of-range-compare) + endif () +endif () + +# platform-specific stuff +if (WIN32) + add_definitions(-DBADVPN_USE_WINAPI -D_WIN32_WINNT=0x600 -DWIN32_LEAN_AND_MEAN) + add_definitions(-DBADVPN_THREAD_SAFE=0) + + set(CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=0x600") + check_symbol_exists(WSAID_WSASENDMSG "winsock2.h;mswsock.h" HAVE_MSW_1) + check_symbol_exists(WSAID_WSARECVMSG "winsock2.h;mswsock.h" HAVE_MSW_2) + check_symbol_exists(WSAID_ACCEPTEX "winsock2.h;mswsock.h" HAVE_MSW_3) + check_symbol_exists(WSAID_GETACCEPTEXSOCKADDRS "winsock2.h;mswsock.h" HAVE_MSW_4) + check_symbol_exists(WSAID_CONNECTEX "winsock2.h;mswsock.h" HAVE_MSW_5) + set(CMAKE_REQUIRED_DEFINITIONS "") + if (NOT (HAVE_MSW_1 AND HAVE_MSW_2 AND HAVE_MSW_3 AND HAVE_MSW_4 AND HAVE_MSW_5)) + add_definitions(-DBADVPN_USE_SHIPPED_MSWSOCK) + check_type_size(WSAMSG HAVE_WSAMSG) + if (NOT HAVE_WSAMSG) + add_definitions(-DBADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG) + endif () + endif () +else () + set(BADVPN_THREADWORK_USE_PTHREAD 1) + add_definitions(-DBADVPN_THREADWORK_USE_PTHREAD) + add_definitions(-DBADVPN_THREAD_SAFE=1) + + link_libraries(rt) + + if (EMSCRIPTEN) + add_definitions(-DBADVPN_EMSCRIPTEN) + add_definitions(-DBADVPN_NO_PROCESS -DBADVPN_NO_UDEV -DBADVPN_NO_RANDOM) + elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_definitions(-DBADVPN_LINUX) + + check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H) + if (HAVE_SYS_SIGNALFD_H) + add_definitions(-DBADVPN_USE_SIGNALFD) + else () + add_definitions(-DBADVPN_USE_SELFPIPE) + endif () + + check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H) + if (HAVE_SYS_EPOLL_H) + add_definitions(-DBADVPN_USE_EPOLL) + else () + add_definitions(-DBADVPN_USE_POLL) + endif () + + check_include_files(linux/rfkill.h HAVE_LINUX_RFKILL_H) + if (HAVE_LINUX_RFKILL_H) + add_definitions(-DBADVPN_USE_LINUX_RFKILL) + set(BADVPN_USE_LINUX_RFKILL 1) + endif () + + check_include_files(linux/input.h HAVE_LINUX_INPUT_H) + if (HAVE_LINUX_INPUT_H) + add_definitions(-DBADVPN_USE_LINUX_INPUT) + set(BADVPN_USE_LINUX_INPUT 1) + endif () + + check_include_files(sys/inotify.h HAVE_SYS_INOTIFY_H) + if (HAVE_SYS_INOTIFY_H) + add_definitions(-DBADVPN_USE_INOTIFY) + set(BADVPN_USE_INOTIFY 1) + endif () + elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + add_definitions(-DBADVPN_FREEBSD) + + check_symbol_exists(kqueue "sys/types.h;sys/event.h;sys/time.h" HAVE_KQUEUE) + if (NOT HAVE_KQUEUE) + message(FATAL_ERROR "kqueue is required") + endif () + add_definitions(-DBADVPN_USE_KEVENT) + endif () + + if (NOT DEFINED BADVPN_WITHOUT_CRYPTODEV) + check_include_files(crypto/cryptodev.h HAVE_CRYPTO_CRYPTODEV_H) + if (HAVE_CRYPTO_CRYPTODEV_H) + add_definitions(-DBADVPN_USE_CRYPTODEV) + elseif (DEFINED BADVPN_WITH_CRYPTODEV) + message(FATAL_ERROR "crypto/cryptodev.h not found") + endif () + endif () +endif () + +# check for syslog +check_include_files(syslog.h HAVE_SYSLOG_H) +if (HAVE_SYSLOG_H) + add_definitions(-DBADVPN_USE_SYSLOG) +endif () + +# add preprocessor definitions +if (BIG_ENDIAN) + add_definitions(-DBADVPN_BIG_ENDIAN) +else () + add_definitions(-DBADVPN_LITTLE_ENDIAN) +endif () + +# install man pages +install( + FILES badvpn.7 + DESTINATION share/man/man7 +) + +# reset variables indicating whether we're building various libraries, +# and set them in the respective CMakeLists files. This is used to disable +# building examples and tests which require libraries that are not available. +set(BUILDING_SECURITY 0) +set(BUILDING_DHCPCLIENT 0) +set(BUILDING_ARPPROBE 0) +set(BUILDING_BKIO 0) +set(BUILDING_PREDICATE 0) +set(BUILDING_UDEVMONITOR 0) +set(BUILDING_THREADWORK 0) +set(BUILDING_RANDOM 0) + +# Used to register an internal library. +# This will also add a library with the -plugin suffix, which is useful +# for use by dynamic libraries (e.g. NCD modules): +# - If BUILD_SHARED_LIBS is off (default), the libraries ${LIB_NAME} and ${LIB_NAME}-plugin +# are built separately. Both are static libraries but the -plugin variant is build as position +# independent code, so it can be (statically) linked into dynamic libraries. +# - If BUILD_SHARED_LIBS is on, only ${LIB_NAME} is built, as a shared library. +# The ${LIB_NAME}-plugin target is set up as an alias to ${LIB_NAME}. +function(badvpn_add_library LIB_NAME LINK_BADVPN_LIBS LINK_SYS_LIBS LIB_SOURCES) + set(BADVPN_LIBS_EXEC) + set(BADVPN_LIBS_PLUGIN) + foreach(LIB ${LINK_BADVPN_LIBS}) + list(APPEND BADVPN_LIBS_EXEC "${LIB}") + list(APPEND BADVPN_LIBS_PLUGIN "${LIB}-plugin") + endforeach() + + add_library("${LIB_NAME}" ${LIB_SOURCES}) + target_link_libraries("${LIB_NAME}" ${BADVPN_LIBS_EXEC} ${LINK_SYS_LIBS}) + set_target_properties("${LIB_NAME}" PROPERTIES OUTPUT_NAME "badvpn-${LIB_NAME}") + + if (BUILD_SHARED_LIBS) + add_library("${LIB_NAME}-plugin" ALIAS "${LIB_NAME}") + else () + add_library("${LIB_NAME}-plugin" STATIC ${LIB_SOURCES}) + target_link_libraries("${LIB_NAME}-plugin" ${BADVPN_LIBS_PLUGIN} ${LINK_SYS_LIBS}) + set_target_properties("${LIB_NAME}-plugin" PROPERTIES OUTPUT_NAME "badvpn-${LIB_NAME}-plugin") + set_target_properties("${LIB_NAME}-plugin" PROPERTIES POSITION_INDEPENDENT_CODE YES) + set_target_properties("${LIB_NAME}-plugin" PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -DBADVPN_PLUGIN") + endif() +endfunction() + +# internal libraries +add_subdirectory(base) +add_subdirectory(system) +add_subdirectory(flow) +add_subdirectory(flowextra) +if (OpenSSL_FOUND) + set(BUILDING_SECURITY 1) + add_subdirectory(security) +endif () +if (NSS_FOUND) + add_subdirectory(nspr_support) +endif () +if (BUILD_CLIENT OR BUILDING_SECURITY) + set(BUILDING_THREADWORK 1) + add_subdirectory(threadwork) +endif () +if (BUILD_CLIENT OR BUILD_TUN2SOCKS) + add_subdirectory(tuntap) +endif () +if (BUILD_SERVER) + set(BUILDING_PREDICATE 1) + add_subdirectory(predicate) +endif () +if (BUILD_CLIENT OR BUILD_FLOODER) + add_subdirectory(server_connection) +endif () +if (BUILD_NCD AND NOT EMSCRIPTEN) + set(BUILDING_DHCPCLIENT 1) + set(BUILDING_ARPPROBE 1) + set(BUILDING_UDEVMONITOR 1) + set(BUILDING_RANDOM 1) + add_subdirectory(stringmap) + add_subdirectory(udevmonitor) + add_subdirectory(dhcpclient) + add_subdirectory(arpprobe) + add_subdirectory(random) +endif () +if (BUILD_TUN2SOCKS) + add_subdirectory(socksclient) + add_subdirectory(udpgw_client) + add_subdirectory(lwip) +endif () +if (BUILD_TUNCTL) + add_subdirectory(tunctl) +endif () + +# example programs +if (BUILD_EXAMPLES) + add_subdirectory(examples) +endif () + +# tests +if (BUILD_TESTS) + add_subdirectory(tests) +endif () + +# server +if (BUILD_SERVER) + add_subdirectory(server) +endif () + +# client +if (BUILD_CLIENT) + add_subdirectory(client) +endif () + +# flooder +if (BUILD_FLOODER) + add_subdirectory(flooder) +endif () + +# tun2socks +if (BUILD_TUN2SOCKS) + add_subdirectory(tun2socks) +endif () + +# udpgw +if (BUILD_UDPGW) + add_subdirectory(udpgw) +endif () + +# ncd +if (BUILD_NCD) + add_subdirectory(ncd) + if (NOT EMSCRIPTEN) + add_subdirectory(ncd-request) + endif () +endif () + +# dostest +if (BUILD_DOSTEST) + add_subdirectory(dostest) +endif () + +message(STATUS "Building components:") + +# print what we're building and what not +foreach (name ${BUILD_COMPONENTS}) + # to lower name + string(TOLOWER "${name}" name_withspaces) + + # append spaces to name + #while (TRUE) + # string(LENGTH "${name_withspaces}" length) + # if (NOT (length LESS 12)) + # break() + # endif () + # set(name_withspaces "${name_withspaces} ") + #endwhile () + + # determine if we're building + if (BUILD_${name}) + set(building "yes") + else () + set(building "no") + endif () + + message(STATUS " ${name_withspaces} ${building}") +endforeach () diff --git a/external/badvpn_dns/COPYING b/external/badvpn_dns/COPYING new file mode 100644 index 00000000..f9733472 --- /dev/null +++ b/external/badvpn_dns/COPYING @@ -0,0 +1,24 @@ +Copyright (c) 2009, Ambroz Bizjak +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/badvpn_dns/ChangeLog b/external/badvpn_dns/ChangeLog new file mode 100644 index 00000000..4c4b96b7 --- /dev/null +++ b/external/badvpn_dns/ChangeLog @@ -0,0 +1,216 @@ +Version 1.999.129: + +- ncd: modules: file_open: Fix typo in assertion. + +- server: Fix bug forgetting to call BSSLConnection_ReleaseBuffers(). Unless threads are enabled, this is an assert failure if NDEBUG is not defined an a non-issue otherwise. + +- ncd: Look for various programs in PATH instead of hardcoded paths. + +- Add compile-udpgw.sh. + +- ncd: modules: net_dns: Implement net.dns.resolvconf() forspecification of arbitrary resolv.conf lines + +Version 1.999.128: + +- tun2socks: add option --append-source-to-username to give the SOCKS server the source IP of the connection + +- tun2socks: IPv6 support, and updated to newer version of lwIP + +- tun2socks: fix some bugs/crashes + +- tun2socks, udpgw: transparent DNS forwarding, though no Windows support on udpgw side (contributed by Kerem Hadimli) + +- NCD: preliminary support for dynamically loading commands + +Version 1.999.127: + +- client, server: implement experimental support for performing SSL operations in worker threads. Currently it's rather inefficient. + +- NCD: modules: value: implement value::append() for appending to a list + +- NCD: modules: net_iptables: add single-argument form of append and insert commands, allowing for generic use + +- NCD: modules: net_iptables: implement net.iptables.insert() and net.ebtables.insert() + +- NCD: modules: sys_start_process: implement options, including username, term_on_deinit and deinit_kill_time + +- NCD: modules: sys_request_server: implement _caller in request handler + +- NCD: modules: add getenv() + +- NCD: modules: daemon: implement options, including username option + +- NCD: modules: runonce: add new options format with a map, implement username option + +- NCD: modules: add buffer(), which exposes a buffer with efficient appending and removing from the beginning. + +- NCD: add a new internal string representation called ComposedString. This allows modules to expose the concatenation of multiple memroy buffers as a single string value, efficiently. + +- fix many, hopefully all, strict aliasing violations. In particular, this fixes a bug where the DHCP client integrated into NCD won't work correctly, subject to optimization flags. + +- NCD: modules: sleep: interpret empty string as no sleeping, add sleep(ms_start) with one argument + +- NCD: modules: add log(), log_r() and log_fr() commands for logging via the BLog system + +Version 1.999.126: + +- NCD: modules: sleep: interpret empty string time as no sleeping, add sleep(ms_start) with one argument + +- NCD: modules: add log module for message logging using the BLog system + +- NCD: implement the "include" and "include_guard" directives, which allow separating reusable code into files + +- NCD: modules: call2: implement call_with_caller_target(), which makes it easier to write reusable code that calls back user-provided code + +- NCD: modules: call2: remove call2_if(), call2_ifelse(), embcall2(), embcall2_if(), embcall2_ifelse() + +- NCD: modules: add sys.start_process(), which implements starting and controlling external processes and reading/writing their stdout/stdin + +- tun2socks: implement SOCKS password authentication + +- NCD: track the depth of values and limit the maximum depth. This avoids stack overflow with very deeply nested values. + +- NCD: modules: add substr() + +- NCD: process_manager: add 2-argument start() method which doesn't take a process identifier + +- NCD: process_manager: allow process identifiers to be any value not just strings + +- NCD: multidepend, depend_scope: fix immediate effect order when a depend finishes backtracking + +- NCD: add depend_scope module to do exactly what the multidepend module does, but with separate non-global dependency name scopes + +- NCD: multidepend: allow dependency names to be any value not just strings + +- NCD: implement value::insert(what) for appending to a list + +- NCD: change the format of addresses in sys.request_server() and sys.request_client() to be the same as in the socket module + +- NCD: add socket module (sys.connect() and sys.listen()) + +- NCD: fix bug where duplicate template/process names would not be detected and weird behaviour would result + +- NCD: add backtrack_point() for simple backtracking + +- NCD: add file_open() for more complete file I/O + +- NCD: implement parse_ipv6_addr() and parse_ipv6_cidr_addr() + +- NCD: port to Emscripten/Javascript, for the in-browser demo + +- NCD: many performance and memory usage improvements + +- NCD: add assert_false() + +- NCD: don't link to OpenSSL to for random number generator. Use /dev/urandom instead to generate XIDs for DHCP. + +- NCD: deprecate ip_in_network() and instead add net.ipv{4,6}.addr_in_network(), net.ipv{4,6}.ifnot_addr_in_network() + +- NCD: implement some IPv6 modules: net.ipv6.addr(), net.ipv6.route() + +- NCD: support CIDR style addr/prefix addresses in various modules + +- NCD: recognize Elif and Else with capital first letter to be consistent with other reserved keywords + +Version 1.999.123: + +- NCD: performance improvements related to finding modules for statements + +- NCD: performance improvements related to resolving object names + +- NCD: performance improvements related to instantiating statement arguments + +- NCD: add value::replace_this() and value::replace_this_undo() + +- NCD: add value::reset() + +- NCD: add value::replace() and value::replace_undo() + +- Port to compile with MSVC for Windows. + +- NCD: add Foreach clause + +- NCD: implement _caller in spawn(), add spawn::join() + +- NCD: add explode() + +- NCD: add hard_reboot() and hard_poweroff() + +- NCD: add file_stat() and file_lstat() + +- NCD: fix regex_replace() semantics. It was very broken because it did a complete replacement pass for every regex on the list, so it would match parts that have already been replaced, producing unexpected results. + +- NCD: small performance improvement + +Version 1.999.121: + +- NCD: improve error handling semantics; see http://code.google.com/p/badvpn/source/detail?r=1376 + +- NCD: fix assertion failure in sys.evdev() if a device error occurs (e.g. device unplugged) while an event is being processed. Similar fix in some other modules, but these may not be reproducable. + +- NCD: some more performance improvements + +- NCD: some performance improvements (~30% faster interpretation of cpu-bound code) + +- NCD: implemented If..elif..else clause. + +- NCD: net.backend.wpa_supplicant: fix to work with wpa_supplicant>=1.0 + +Version 1.999.115: + +- NCD: Many improvements; new statements, including call(), alias(), foreach(), choose(). + +Version 1.999.113: + +- NCD: when starting child processes, make sure that file descriptors for standard + streams are always open in the child, by opening /dev/null if they are not. + +- Improve build system to allow selective building of components. + By default, everything is built, unless -DBUILD_NOTHING_BY_DEFAULT=1 is given. + Individual components can then be enabled or disabled using -DBUILD_COMPONENT=1 + and -DBUILD_COMPONENT=0. + +- When starting any BadVPN program, make sure that file descriptors for standard + streams are always open in the child, by opening /dev/null if they are not. + +- NCD: net.backend.wpa_supplicant(): add 'bssid' and 'ssid' variables to allow + determining what wireless network wpa_supplicant connected to. + +- NCD: net.backend.wpa_supplicant(): do not require the user to start wpa_supplicant via + stdbuf, but do it automatically. + +Version 1.999.111: + +- Improved protocol such that peers can use SSL when comminicating via the server. This + improves security, as compromising the server will not allow the attacker to see secret + data shared by peers (in particular, encryption keys and OTP seeds when in UDP mode). + + Compatibility is preserved if an only if the following conditions are met: + - The server is using the latest version. + - If the network is using SSL, all clients using the new version are using the + "--allow-peer-talk-without-ssl" command line option. + + Be aware, however, that using the "--allow-peer-talk-without-ssl" option negates the + security benefits of the new SSL support - not only between pairs of peers where one + peer is using the old version, but also between pairs where both peers are capable + of SSL. This is because the server can re-initialize the pair, telling them not to use + SSL. + +Version 1.999.107: + +- Added Windows IOCP support, removing the limitation on ~64 connections. This is important + for tun2socks, which may have to handle several hundred connections. + +Version 1.999.105.2: + +- Fixed an assertion failure in tun2socks related to sending data to SOCKS. + +Version 1.999.101.3: + +- Fixed UDP transport on Windows 7 which didn't work (was only tested on XP). + +Version 1.999.101: + +- Fixed a protocol issue present in versions <=1.999.100.3. Compatibility is preserved in + case of a new server and old clients, but it is not possible to connect to an old server + with a new client. diff --git a/external/badvpn_dns/INSTALL b/external/badvpn_dns/INSTALL new file mode 100644 index 00000000..3605f4e5 --- /dev/null +++ b/external/badvpn_dns/INSTALL @@ -0,0 +1,76 @@ +1 Requirements + +1.1 Operating system + +Linux: +- Linux kernel 2.6. Kernel 2.4 will work, but performance will suffer. +- tested on x86, x86_64 and ARM architectures. Not tested on any big-endian architecture. + +Windows: +- Windows XP or newer; tested on Windows XP and Windows 7 + +FreeBSD: +- Not regularly tested. + +Other systems are not supported. + +1.2 Compilers + +Linux: + - gcc + - clang, except >=3.0 (clang bug http://llvm.org/bugs/show_bug.cgi?id=11535) + +Windows: + - gcc from the mingw-w64 project for 32-bit targets + +C language features used: + - Standard (all part of C99): + - designated initializers + - stdint.h, inttypes.h, stddef.h + - intermingled declarations and code + - for loop initial declaration + - one-line "//" comments + - Extensions: + - packed structure attribute (to pack a structure and allow unaligned access) + +1.3 CMake + +The build system uses CMake. + +1.4 OpenSSL + +Libcrypto (part of OpenSSL) is used for block ciphers, hash functions and random data generation. + +1.5 Network Security Services (NSS) + +The NSS library from Mozilla is used for TLS support. NSS command-line tools are also needed +for setting up certificates. + +1.6 TAP-Win32 (Windows only) (runtime only) + +The TAP-Win32 driver, part of OpenVPN. + +2 Compilation + +2.1 Compiling on Linux + +$ tar xf badvpn-.tar.bz2 +$ mkdir build +$ cd build +$ cmake ../badvpn- -DCMAKE_INSTALL_PREFIX=/usr/local +$ make +If you want to install it, run as root: +# make install + +If you only want NCD or tun2socks and not the VPN system, you can avoid the NSS dependency by passing +the following to the cmake command: +-DBUILD_NCD=1 -DBUILD_TUN2SOCKS=1 -DBUILD_NOTHING_BY_DEFAULT=1 + +2.2 Compiling for Windows + +See the file INSTALL-WINDOWS for detailed instructions. + +3 Usage + +The primary documentation is on the BadVPN homepage, http://code.google.com/p/badvpn/ . +Additionally, some man pages are installed (badvpn(7), badvpn-server(8), badvpn-client(8)). diff --git a/external/badvpn_dns/INSTALL-WINDOWS b/external/badvpn_dns/INSTALL-WINDOWS new file mode 100644 index 00000000..9f0d5cf1 --- /dev/null +++ b/external/badvpn_dns/INSTALL-WINDOWS @@ -0,0 +1,72 @@ +There are many ways to build BadVPN for Windows. It can be built with MSVC or GCC compilers, +and it be built natively from Windows or cross-compiled from Linux. However, this document +only describes building natively from Windows using MSVC. + +1. Get a MSVC compiler, e.g. from Visual Studio, Visual Studio Express or from the Windows SDK. + +2. Choose a directory where built stuff will be installed into; we call it . + +3. Build the NSS library. + NOTE: you can also use the prebuilt version in the BadVPN windows download. + + - Install MozillaBuild: + http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/win32/MozillaBuildSetup-Latest.exe . + + - Download the NSS source code that includes NSPR. As of the time of writing the latest version was 3.13.5: + https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_13_5_RTM/src/nss-3.13.5-with-nspr-4.9.1.tar.gz . + + Extract it to c:\ so that you have C:\mozilla . + + - Open a terminal with access to the Visual Studio compilers and other tools. E.g. if you use the Windows SDK, + activate the following start menu item: Programs -> Microsoft Windows SDK v7.1 -> Windows SDK 7.1 Command Prompt. + + - In this terminal, run: + + > c:\mozilla-build\start-l10n.bat + + - Either a new terminal opens with a bash shell, or a bash shell starts in the existing terminal. Either way, + enter the following commands to finally build NSS: (here paths are written as /driveletter/...) + + $ export OS_TARGET=WINNT + $ export BUILD_OPT=1 + $ cd /mozilla/security/nss + $ make nss_build_all + + Now use a script shipped with the BadVPN source to copy the resulting files into appropriate directories within : + + $ /scripts/copy_nss ../../dist + +4. Build the OpenSSL library. + NOTE: you can also use the prebuilt version in the BadVPN windows download. + + - Install ActivePerl. + + - Download the OpenSSL source code and extract it. + + - Open a compiler terminal, as was done when building NSS. Inside it, run: + + > cd + > perl Configure VC-WIN32 --prefix= + > ms\do_ms + > nmake -f ms\ntdll.mak + + To copy the results into : + + > nmake -f ms\ntdll.mak install + +5. Build BadVPN. + + - Install CMake. During installation, select the option to include cmake in PATH + to avoid having to type a long path into the terminal. + + - Create an empty folder where BadVPN will be built; call it . + + - Open a compiler terminal. Inside it, run: + + > cd + > cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release + > nmake + + To copy the results into : + + > nmake install diff --git a/external/badvpn_dns/arpprobe/BArpProbe.c b/external/badvpn_dns/arpprobe/BArpProbe.c new file mode 100644 index 00000000..2e6feb4f --- /dev/null +++ b/external/badvpn_dns/arpprobe/BArpProbe.c @@ -0,0 +1,359 @@ +/** + * @file BArpProbe.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "BArpProbe.h" + +#include + +#define STATE_INITIAL 1 +#define STATE_NOEXIST 2 +#define STATE_EXIST 3 +#define STATE_EXIST_PANIC 4 + +static void dgram_handler (BArpProbe *o, int event) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "packet socket error"); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BARPPROBE_EVENT_ERROR)); + return; +} + +static void send_request (BArpProbe *o) +{ + if (o->send_sending) { + BLog(BLOG_ERROR, "cannot send packet while another packet is being sent!"); + return; + } + + // build packet + struct arp_packet *arp = &o->send_packet; + arp->hardware_type = hton16(ARP_HARDWARE_TYPE_ETHERNET); + arp->protocol_type = hton16(ETHERTYPE_IPV4); + arp->hardware_size = hton8(6); + arp->protocol_size = hton8(4); + arp->opcode = hton16(ARP_OPCODE_REQUEST); + memcpy(arp->sender_mac, o->if_mac, 6); + arp->sender_ip = hton32(0); + memset(arp->target_mac, 0, sizeof(arp->target_mac)); + arp->target_ip = o->addr; + + // send packet + PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)&o->send_packet, sizeof(o->send_packet)); + + // set sending + o->send_sending = 1; +} + +static void send_if_handler_done (BArpProbe *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send_sending) + + // set not sending + o->send_sending = 0; +} + +static void recv_if_handler_done (BArpProbe *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= sizeof(struct arp_packet)) + + // receive next packet + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet); + + if (data_len != sizeof(struct arp_packet)) { + BLog(BLOG_WARNING, "receive: wrong size"); + return; + } + + struct arp_packet *arp = &o->recv_packet; + + if (ntoh16(arp->hardware_type) != ARP_HARDWARE_TYPE_ETHERNET) { + BLog(BLOG_WARNING, "receive: wrong hardware type"); + return; + } + + if (ntoh16(arp->protocol_type) != ETHERTYPE_IPV4) { + BLog(BLOG_WARNING, "receive: wrong protocol type"); + return; + } + + if (ntoh8(arp->hardware_size) != 6) { + BLog(BLOG_WARNING, "receive: wrong hardware size"); + return; + } + + if (ntoh8(arp->protocol_size) != 4) { + BLog(BLOG_WARNING, "receive: wrong protocol size"); + return; + } + + if (ntoh16(arp->opcode) != ARP_OPCODE_REPLY) { + return; + } + + if (arp->sender_ip != o->addr) { + return; + } + + int old_state = o->state; + + // set minus one missed + o->num_missed = -1; + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITSEND); + + // set state exist + o->state = STATE_EXIST; + + // report exist if needed + if (old_state == STATE_INITIAL || old_state == STATE_NOEXIST) { + o->handler(o->user, BARPPROBE_EVENT_EXIST); + return; + } +} + +static void timer_handler (BArpProbe *o) +{ + DebugObject_Access(&o->d_obj); + + // send request + send_request(o); + + switch (o->state) { + case STATE_INITIAL: { + ASSERT(o->num_missed >= 0) + ASSERT(o->num_missed < BARPPROBE_INITIAL_NUM_ATTEMPTS) + + // increment missed + o->num_missed++; + + // all attempts failed? + if (o->num_missed == BARPPROBE_INITIAL_NUM_ATTEMPTS) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + + // set state noexist + o->state = STATE_NOEXIST; + + // report noexist + o->handler(o->user, BARPPROBE_EVENT_NOEXIST); + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV); + } break; + + case STATE_NOEXIST: { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + } break; + + case STATE_EXIST: { + ASSERT(o->num_missed >= -1) + ASSERT(o->num_missed < BARPPROBE_EXIST_NUM_NOREPLY) + + // increment missed + o->num_missed++; + + // all missed? + if (o->num_missed == BARPPROBE_EXIST_NUM_NOREPLY) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV); + + // set zero missed + o->num_missed = 0; + + // set state panic + o->state = STATE_EXIST_PANIC; + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITRECV); + } break; + + case STATE_EXIST_PANIC: { + ASSERT(o->num_missed >= 0) + ASSERT(o->num_missed < BARPPROBE_EXIST_PANIC_NUM_NOREPLY) + + // increment missed + o->num_missed++; + + // all missed? + if (o->num_missed == BARPPROBE_EXIST_PANIC_NUM_NOREPLY) { + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV); + + // set state panic + o->state = STATE_NOEXIST; + + // report noexist + o->handler(o->user, BARPPROBE_EVENT_NOEXIST); + return; + } + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV); + } break; + } +} + +int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) +{ + ASSERT(ifname) + ASSERT(handler) + + // init arguments + o->addr = addr; + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // get interface information + int if_mtu; + int if_index; + if (!badvpn_get_iface_info(ifname, o->if_mac, &if_mtu, &if_index)) { + BLog(BLOG_ERROR, "failed to get interface information"); + goto fail0; + } + + uint8_t *if_mac = o->if_mac; + BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d", + if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index); + + // check MTU + if (if_mtu < sizeof(struct arp_packet)) { + BLog(BLOG_ERROR, "MTU is too small for ARP !?!"); + goto fail0; + } + + // init dgram + if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) { + BLog(BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // bind dgram + BAddr bind_addr; + BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac); + if (!BDatagram_Bind(&o->dgram, bind_addr)) { + BLog(BLOG_ERROR, "BDatagram_Bind failed"); + goto fail1; + } + + // set dgram send addresses + BAddr dest_addr; + uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac); + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr); + + // init send interface + BDatagram_SendAsync_Init(&o->dgram, sizeof(struct arp_packet)); + o->send_if = BDatagram_SendAsync_GetIf(&o->dgram); + PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_if_handler_done, o); + + // set not sending + o->send_sending = 0; + + // init recv interface + BDatagram_RecvAsync_Init(&o->dgram, sizeof(struct arp_packet)); + o->recv_if = BDatagram_RecvAsync_GetIf(&o->dgram); + PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_if_handler_done, o); + + // init timer + BTimer_Init(&o->timer, 0, (BTimer_handler)timer_handler, o); + + // receive first packet + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet); + + // send request + send_request(o); + + // set timer + BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV); + + // set zero missed + o->num_missed = 0; + + // set state initial + o->state = STATE_INITIAL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void BArpProbe_Free (BArpProbe *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free timer + BReactor_RemoveTimer(o->reactor, &o->timer); + + // free recv interface + BDatagram_RecvAsync_Free(&o->dgram); + + // free send interface + BDatagram_SendAsync_Free(&o->dgram); + + // free dgram + BDatagram_Free(&o->dgram); +} diff --git a/external/badvpn_dns/arpprobe/BArpProbe.h b/external/badvpn_dns/arpprobe/BArpProbe.h new file mode 100644 index 00000000..2ec3ffae --- /dev/null +++ b/external/badvpn_dns/arpprobe/BArpProbe.h @@ -0,0 +1,80 @@ +/** + * @file BArpProbe.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BARPPROBE_H +#define BADVPN_BARPPROBE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BARPPROBE_INITIAL_WAITRECV 1000 +#define BARPPROBE_INITIAL_NUM_ATTEMPTS 6 +#define BARPPROBE_NOEXIST_WAITRECV 15000 +#define BARPPROBE_EXIST_WAITSEND 15000 +#define BARPPROBE_EXIST_WAITRECV 10000 +#define BARPPROBE_EXIST_NUM_NOREPLY 2 +#define BARPPROBE_EXIST_PANIC_WAITRECV 1000 +#define BARPPROBE_EXIST_PANIC_NUM_NOREPLY 6 + +#define BARPPROBE_EVENT_EXIST 1 +#define BARPPROBE_EVENT_NOEXIST 2 +#define BARPPROBE_EVENT_ERROR 3 + +typedef void (*BArpProbe_handler) (void *user, int event); + +typedef struct { + uint32_t addr; + BReactor *reactor; + void *user; + BArpProbe_handler handler; + BDatagram dgram; + uint8_t if_mac[6]; + PacketPassInterface *send_if; + int send_sending; + struct arp_packet send_packet; + PacketRecvInterface *recv_if; + struct arp_packet recv_packet; + BTimer timer; + int state; + int num_missed; + DebugError d_err; + DebugObject d_obj; +} BArpProbe; + +int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) WARN_UNUSED; +void BArpProbe_Free (BArpProbe *o); + +#endif diff --git a/external/badvpn_dns/arpprobe/CMakeLists.txt b/external/badvpn_dns/arpprobe/CMakeLists.txt new file mode 100644 index 00000000..a090f107 --- /dev/null +++ b/external/badvpn_dns/arpprobe/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(arpprobe "base;system;flow" "" BArpProbe.c) diff --git a/external/badvpn_dns/badvpn.7 b/external/badvpn_dns/badvpn.7 new file mode 100644 index 00000000..c421a35b --- /dev/null +++ b/external/badvpn_dns/badvpn.7 @@ -0,0 +1,324 @@ +.TH badvpn 7 "6 October 2010" +.SH NAME +BadVPN - peer-to-peer VPN system +.SH DESCRIPTION +.P +BadVPN is a peer-to-peer VPN system. It provides a Layer 2 (Ethernet) network between +the peers (VPN network nodes). The peers connect to a central server which acts as a chat +server for them to establish direct connections between each other (data connections). +These connections are used for transferring network data (Ethernet frames). +.SS "Features" +.P +.B "Data connections" +.P +Peers can transfer network data either over UDP or TCP. For both there are ways of +securing the data (see below). +.P +.B "IPv6 support" +.P +IPv6 can be used for both server connections and data connections, alongside with IPv4. +Additionally, both can be combined to allow gradual migration to IPv6. +.P +.B "Address selection" +.P +Because NATs and firewalls are widespread, it is harder for peer-to-peer services to operate. +In general, for two computers to be able to communicate, one computer must +.I bind +to one of its addresses, and the other computer must +.I connect +to the computer that binded (both for TCP and UDP). In a network with point-to-point +connectivity, the connecting computer can connect to the same address as the binding computer +bound to, so it is sufficient for the binding computer to send its address to the connecting +computer. However, NATs and firewalls break point-to-point connectivity. When a network is +behind a NAT, it is, by default, impossible for computers outside of that network to connect +to computers inside the network. This is because computers inside the network have no externally +visible IP address, and only communicate with the outside world through the external IP address +of the NAT router. It is however possible to manually configure the NAT router to +.I forward +a specific port number on its external IP address to a specific computer inside the network. +This makes it possible for a computer outside of the network to connect to a computer inside +a network, however, it must connect to the external address of the NAT router (rather than +the address the computer inside bound to, which is its internal address). So there needs +to be some way for the connecting peer to know what address to connect to. +.P +BadVPN solves this problem with so-called +.IR "address scopes" "." +The peer that binds must have a list of external addresses for each address it can bind to, +possibly ordered from best to worst. Each external address has its scope name. A scope name +represents part of a network from which an external address can be reached. On the other hand, +the peer that connects must have a list of scopes which it can reach. When a peer binds to an +address, it sends the other peer a list of external addresses along with scope names. That peer +than chooses the first external address whose scope it recognizes and attempts to connect to it +(if there is one). +.P +BadVPN also allows a peer to have multiple addresses for binding to. It is possible to specify +both an IPv4 and an IPv6 address to work in a multi-protocol environment. +.P +.B "Relaying" +.P +BadVPN can be configured to allow pairs of peers that cannot communicate directly (i.e. because of +NATs or firewalls) to relay network data through a third peer. Relaying is only attempted if +none of the two peers recognize any of the other peer's external addresses (or there are none). +For relaying to work, for each of the two peers (P1, other one P2) there must be at least one +third peer (R) that P1 it is allowed to relay through and can communicate directly with, and all +such peers R must be able to communicate directly with P2. +.P +.B "IGMP snooping" +.P +BadVPN nodes perform IGMP snooping in order to efficiently deliver multicast frames. For example, +this makes it possible to use BadVPN as a tunnel into an IPTV network of an Internet Service Provider +for you to watch TV from wherever you want (given sufficient link quality). +.P +.B "Code quality" +.P +BadVPN has great focus on code quality and reliability. BadVPN is written in the C programming +language. It is a single-threaded event-driven program. This allows for low resource usage and +fast response times. Even though C is a relatively low-level language, the programs are made of +small, highly cohesive and loosely coupled modules that are combined into a complete program on +a high level. Modules are accesed and communicate through small, simple and to-the-point interfaces. +It utilizes a flow-based design which greatly simplifies processing of data and input and output +of the programs. +.SS "Security features" +.P +BadVPN contains many security features, all of which are optional. The included security +features are described here. +.P +.B TLS for client-server connections +.P +It is possible for the peers to communicate with the chat server securely with TLS. It is +highly recommended that this feature is used if any security whatsoever is needed. Not +using it renders all other security features useless, since clients exchange keys +unencrypted via the server. When enabled, the chat server requires each client to identify +itself with a certificate. +.P +BadVPN uses Mozilla's NSS library for TLS support. This means that the required certificates +and keys must be available in a NSS database. The database and certificates can be +generated with the +.B certutil +command. See the examples section on how to generate and distribute the certificates. +.P +.B TLS for peer messaging +.P +If TLS is being used for client-server connections, it will also be used between each pair of +peers communicating via the server, on top of the TLS connections to the server. This secures +the messages from the server itself. It is important because the messages may include +encryption keys and other private data. +.P +.B TLS for TCP data connections +.P +If TCP is used for data connections between the peers, the data connections can be secured +with TLS. This requires using TLS for client-server connections. The clients need to trust +each others' certificates to be able to connect. Additionally, each client must identify to +its peers with the same certificates it used for connecting to the server. +.P +.B Encryption for UDP data connections +.P +If UDP is used for data connections, it is possible for each pair of peers to encrypt their +UDP packets with a symmetric block cipher. Note that the encryption keys are transmitted +through the server unencrypted, so for this to be useful, server connections must be secured +with TLS. The encryption aims to prevent third parties from seeing the real contents of +the network data being transfered. +.P +.B Hashes for UDP data connections +.P +If UDP is used for data connections, it is possible to include hashes in packets. Note that +hashes are only useful together with encryption. If enabled, the hash is calculated on the +packet with the hash field zeroed and then written to the hash field. Hashes are calculated +and included before encryption (if enabled). Combined with encryption, hashes aim to prevent +third parties from tampering with the packets and injecting them into the network. +.P +.B One-time passwords for UDP data connections +.P +If UDP is used for data connections, it is possible to include one-time passwords in packets. +Note that for this to be useful, server connections must be secured with TLS. +One-time passwords are generated from a seed value by encrypting zero data with a block cipher. +The seed contains the encryption key for the block cipher and the initialization vector. +Only a fixed number of passwords are used from a single seed. The peers exchange seeds through +the server. One-time passwords aim to prevent replay attacks. +.P +.B Control over peer communication +.P +It is possible to instruct the chat server to only allow certain peers to communicate. This +will break end-to-end connectivity in the virtual network. It is useful in certain cases +to improve security, for example when the VPN is used only to allow clients to securely connect +to a central service. +.SH "EXAMPLES" +.SS "Setting up certificates" +.P +If you want to use TLS for server connections (recommended), the server and all the peers will +need certificates. This section explains how to generate and distribute the certificates using +NSS command line tools. +.P +.B Setting up the Certificate Authority (CA) +.P +On the system that will host the CA, create a NSS database for the CA and generate a CA certificate +valid for 24 months: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -N +.br +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "vpnca" -s "CN=vpnca" -t "TC,," -x -2 -v 24 +.br +> Is this a CA certificate [y/N]? y +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > -1 +.br +> Is this a critical extension [y/N]? n +.P +Export the public CA certificate (this file is public): +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -L -n vpnca -a > ca.pem +.P +.B Setting up the server certificate +.P +On the CA system, generate a certificate for the server valid for 24 months, with TLS server usage context: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "" -s "CN=" -c "vpnca" -t ",," -2 -6 -v 24 +.br +> 0 +.br +> -1 +.br +> Is this a critical extension [y/N]? n +.br +> Is this a CA certificate [y/N]? n +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > +.br +> Is this a critical extension [y/N]? n +.P +Export the server certificate to a PKCS#12 file (this file must be kept secret): +.P +vpnca $ pk12util -d sql:/home/vpnca/nssdb -o server.p12 -n "" +.P +On the system that will run the server, create a NSS database and import the CA certificate +and the server cerificate: +.P +vpnserver $ certutil -d sql:/home/vpnserver/nssdb -N +.br +vpnserver $ certutil -d sql:/home/vpnserver/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem +.br +vpnserver $ pk12util -d sql:/home/vpnserver/nssdb -i /path/to/server.p12 +.P +.B Setting up peer certificates +.P +On the CA system, generate a certificate for the peer valid for 24 months, with TLS client and +TLS server usage contexts: +.P +vpnca $ certutil -d sql:/home/vpnca/nssdb -S -n "peer-" -s "CN=peer-" -c "vpnca" -t ",," -2 -6 -v 24 +.br +> 0 +.br +> 1 +.br +> -1 +.br +> Is this a critical extension [y/N]? n +.br +> Is this a CA certificate [y/N]? n +.br +> Enter the path length constraint, enter to skip [<0 for unlimited path]: > +.br +> Is this a critical extension [y/N]? n +.P +Export the peer certificate to a PKCS#12 file (this file must be kept secret): +.P +vpnca $ pk12util -d sql:/home/vpnca/nssdb -o peer-.p12 -n "peer-" +.P +On the system that will run the VPN client, create a NSS database and import the CA certificate +and the peer cerificate: +.P +vpnclient $ certutil -d sql:/home/vpnclient/nssdb -N +.br +vpnclient $ certutil -d sql:/home/vpnclient/nssdb -A -t "CT,," -n "vpnca" -i /path/to/ca.pem +.br +vpnclient $ pk12util -d sql:/home/vpnclient/nssdb -i /path/to/peer-.p12 +.SS "Setting up TAP devices" +.P +You need to create and configure TAP devices on all computers that will participate in the virtual network +(i.e. run the client program). See +.BR badvpn-client (8), +section `TAP DEVICE CONFIGURATION` for details. +.SS "Example: Local IPv4 network, UDP transport, zero security" +.P +.B Starting the server: +.P +badvpn-server --listen-addr 0.0.0.0:7000 +.P +.B Starting the peers: +.P +badvpn-client +.RS +--server-addr :7000 +.br +--transport-mode udp --encryption-mode none --hash-mode none +.br +--scope local1 +.br +--bind-addr 0.0.0.0:8000 --num-ports 30 --ext-addr {server_reported}:8000 local1 +.br +--tapdev tap0 +.RE +.SS "Example: Adding TLS and UDP security" +.P +.B Starting the server (other options as above): +.P +badvpn-server ... +.RS +--ssl --nssdb sql:/home/vpnserver/nssdb --server-cert-name "" +.RE +.P +.B Starting the peers (other options as above): +.P +badvpn-client ... +.RS +--ssl --nssdb sql:/home/vpnclient/nssdb --client-cert-name "peer-" +.br +--encryption-mode blowfish --hash-mode md5 --otp blowfish 3000 2000 +.RE +.SS "Example: Multiple local networks behind NATs, all connected to the Internet" +.P +For each peer in the existing local network, configure the NAT router to forward its +range of ports to it (assuming their port ranges do not overlap). The clients also need +to know the external IP address of the NAT router. If you don't have a static one, +you'll need to discover it before starting the clients. Also forward the server port to +the server. +.P +.B Starting the peers in the local network (other options as above): +.P +badvpn-client +.RS +.RB "..." +.br +--scope internet +.br +.RB "..." +.br +--ext-addr : internet +.br +.RB "..." +.RE +.P +The --ext-addr option applies to the previously specified --bind-addr option, and must come after +the first --ext-addr option which specifies a local address. +.P +Now perform a similar setup in some other local network behind a NAT. However: +.br +- Don't set up a new server, instead make the peers connect to the existing server in the first +local network. +.br +- You can't use {server_reported} for the local address --ext-addr options, because the server +would report the NAT router's external address rather than the peer's internal address. Instead +each peer has to know its internal IP address. +.br +- Use a different scope name for it, e.g. "local2" instead of "local1". +.P +If setup correctly, all peers will be able to communicate: those in the same local network will +communicate directly through local addresses, and those in different local networks will +communicate through the Internet. +.SH "PROTOCOL" +The protocols used in BadVPN are described in the source code in the protocol/ directory. +.SH "SEE ALSO" +.BR badvpn-server (8), +.BR badvpn-client (8) +.SH AUTHORS +Ambroz Bizjak diff --git a/external/badvpn_dns/base/BLog.c b/external/badvpn_dns/base/BLog.c new file mode 100644 index 00000000..94242d57 --- /dev/null +++ b/external/badvpn_dns/base/BLog.c @@ -0,0 +1,96 @@ +/** + * @file BLog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "BLog.h" + +#ifndef BADVPN_PLUGIN + +struct _BLog_channel blog_channel_list[] = { +#include +}; + +struct _BLog_global blog_global = { + #ifndef NDEBUG + 0 + #endif +}; + +#endif + +// keep in sync with level numbers in BLog.h! +static char *level_names[] = { NULL, "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" }; + +static void stdout_log (int channel, int level, const char *msg) +{ + fprintf(stdout, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +} + +static void stderr_log (int channel, int level, const char *msg) +{ + fprintf(stderr, "%s(%s): %s\n", level_names[level], blog_global.channels[channel].name, msg); +} + +static void stdout_stderr_free (void) +{ +} + +void BLog_InitStdout (void) +{ + BLog_Init(stdout_log, stdout_stderr_free); +} + +void BLog_InitStderr (void) +{ + BLog_Init(stderr_log, stdout_stderr_free); +} + +// ==== PSIPHON ==== +#ifdef PSIPHON + +void PsiphonLog(const char *level, const char *channel, const char *msg); + +static void psiphon_log (int channel, int level, const char *msg) +{ + PsiphonLog(level_names[level], blog_global.channels[channel].name, msg); +} + +static void psiphon_free (void) +{ +} + +void BLog_InitPsiphon (void) +{ + BLog_Init(psiphon_log, psiphon_free); +} + +#endif +// ==== PSIPHON ==== diff --git a/external/badvpn_dns/base/BLog.h b/external/badvpn_dns/base/BLog.h new file mode 100644 index 00000000..dd2e4d0c --- /dev/null +++ b/external/badvpn_dns/base/BLog.h @@ -0,0 +1,402 @@ +/** + * @file BLog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for logging. + */ + +#ifndef BADVPN_BLOG_H +#define BADVPN_BLOG_H + +#include +#include + +#include +#include + +// auto-generated channel numbers and number of channels +#include + +// keep in sync with level names in BLog.c! +#define BLOG_ERROR 1 +#define BLOG_WARNING 2 +#define BLOG_NOTICE 3 +#define BLOG_INFO 4 +#define BLOG_DEBUG 5 + +#define BLog(...) BLog_LogToChannel(BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BContextLog(context, ...) BLog_ContextLog((context), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define BLOG_CCCC(context) BLog_MakeChannelContext((context), BLOG_CURRENT_CHANNEL) + +typedef void (*_BLog_log_func) (int channel, int level, const char *msg); +typedef void (*_BLog_free_func) (void); + +struct _BLog_channel { + const char *name; + int loglevel; +}; + +struct _BLog_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + struct _BLog_channel channels[BLOG_NUM_CHANNELS]; + _BLog_log_func log_func; + _BLog_free_func free_func; + BMutex mutex; +#ifndef NDEBUG + int logging; +#endif + char logbuf[2048]; + int logbuf_pos; +}; + +extern struct _BLog_channel blog_channel_list[]; +extern struct _BLog_global blog_global; + +typedef void (*BLog_logfunc) (void *); + +typedef struct { + BLog_logfunc logfunc; + void *logfunc_user; +} BLogContext; + +typedef struct { + BLogContext context; + int channel; +} BLogChannelContext; + +static int BLogGlobal_GetChannelByName (const char *channel_name); + +static void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func); +static void BLog_Free (void); +static void BLog_SetChannelLoglevel (int channel, int loglevel); +static int BLog_WouldLog (int channel, int level); +static void BLog_Begin (void); +static void BLog_AppendVarArg (const char *fmt, va_list vl); +static void BLog_Append (const char *fmt, ...); +static void BLog_AppendBytes (const char *data, size_t len); +static void BLog_Finish (int channel, int level); +static void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl); +static void BLog_LogToChannel (int channel, int level, const char *fmt, ...); +static void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl); +static void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...); +static BLogContext BLog_RootContext (void); +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user); +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl); +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...); +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel); +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl); +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...); + +void BLog_InitStdout (void); +void BLog_InitStderr (void); + +// PSIPHON +void BLog_InitPsiphon (void); + +int BLogGlobal_GetChannelByName (const char *channel_name) +{ + int i; + for (i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (!strcmp(blog_channel_list[i].name, channel_name)) { + return i; + } + } + + return -1; +} + +void BLog_Init (_BLog_log_func log_func, _BLog_free_func free_func) +{ + ASSERT(!blog_global.initialized) + + #ifndef NDEBUG + blog_global.initialized = 1; + #endif + + // initialize channels + memcpy(blog_global.channels, blog_channel_list, BLOG_NUM_CHANNELS * sizeof(struct _BLog_channel)); + + blog_global.log_func = log_func; + blog_global.free_func = free_func; +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + ASSERT_FORCE(BMutex_Init(&blog_global.mutex)) +} + +void BLog_Free (void) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(!blog_global.logging) +#endif + + BMutex_Free(&blog_global.mutex); + + #ifndef NDEBUG + blog_global.initialized = 0; + #endif + + blog_global.free_func(); +} + +void BLog_SetChannelLoglevel (int channel, int loglevel) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(loglevel >= 0 && loglevel <= BLOG_DEBUG) + + blog_global.channels[channel].loglevel = loglevel; +} + +int BLog_WouldLog (int channel, int level) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + return (level <= blog_global.channels[channel].loglevel); +} + +void BLog_Begin (void) +{ + ASSERT(blog_global.initialized) + + BMutex_Lock(&blog_global.mutex); + +#ifndef NDEBUG + ASSERT(!blog_global.logging) + blog_global.logging = 1; +#endif +} + +void BLog_AppendVarArg (const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + int w = vsnprintf(blog_global.logbuf + blog_global.logbuf_pos, sizeof(blog_global.logbuf) - blog_global.logbuf_pos, fmt, vl); + + if (w >= sizeof(blog_global.logbuf) - blog_global.logbuf_pos) { + blog_global.logbuf_pos = sizeof(blog_global.logbuf) - 1; + } else { + blog_global.logbuf_pos += w; + } +} + +void BLog_Append (const char *fmt, ...) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + + va_list vl; + va_start(vl, fmt); + BLog_AppendVarArg(fmt, vl); + va_end(vl); +} + +void BLog_AppendBytes (const char *data, size_t len) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + + size_t avail = (sizeof(blog_global.logbuf) - 1) - blog_global.logbuf_pos; + len = (len > avail ? avail : len); + + memcpy(blog_global.logbuf + blog_global.logbuf_pos, data, len); + blog_global.logbuf_pos += len; + blog_global.logbuf[blog_global.logbuf_pos] = '\0'; +} + +void BLog_Finish (int channel, int level) +{ + ASSERT(blog_global.initialized) +#ifndef NDEBUG + ASSERT(blog_global.logging) +#endif + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + ASSERT(BLog_WouldLog(channel, level)) + + ASSERT(blog_global.logbuf_pos >= 0) + ASSERT(blog_global.logbuf_pos < sizeof(blog_global.logbuf)) + ASSERT(blog_global.logbuf[blog_global.logbuf_pos] == '\0') + + blog_global.log_func(channel, level, blog_global.logbuf); + +#ifndef NDEBUG + blog_global.logging = 0; +#endif + blog_global.logbuf_pos = 0; + blog_global.logbuf[0] = '\0'; + + BMutex_Unlock(&blog_global.mutex); +} + +void BLog_LogToChannelVarArg (int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogToChannel (int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +void BLog_LogViaFuncVarArg (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, va_list vl) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); +} + +void BLog_LogViaFunc (BLog_logfunc func, void *arg, int channel, int level, const char *fmt, ...) +{ + ASSERT(blog_global.initialized) + ASSERT(channel >= 0 && channel < BLOG_NUM_CHANNELS) + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + if (!BLog_WouldLog(channel, level)) { + return; + } + + va_list vl; + va_start(vl, fmt); + + BLog_Begin(); + func(arg); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(channel, level); + + va_end(vl); +} + +static void BLog__root_logfunc (void *unused) +{ +} + +static BLogContext BLog_RootContext (void) +{ + return BLog_MakeContext(BLog__root_logfunc, NULL); +} + +static BLogContext BLog_MakeContext (BLog_logfunc logfunc, void *logfunc_user) +{ + ASSERT(logfunc) + + BLogContext context; + context.logfunc = logfunc; + context.logfunc_user = logfunc_user; + return context; +} + +static void BLog_ContextLogVarArg (BLogContext context, int channel, int level, const char *fmt, va_list vl) +{ + BLog_LogViaFuncVarArg(context.logfunc, context.logfunc_user, channel, level, fmt, vl); +} + +static void BLog_ContextLog (BLogContext context, int channel, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ContextLogVarArg(context, channel, level, fmt, vl); + va_end(vl); +} + +static BLogChannelContext BLog_MakeChannelContext (BLogContext context, int channel) +{ + BLogChannelContext ccontext; + ccontext.context = context; + ccontext.channel = channel; + return ccontext; +} + +static void BLog_ChannelContextLogVarArg (BLogChannelContext ccontext, int level, const char *fmt, va_list vl) +{ + BLog_ContextLogVarArg(ccontext.context, ccontext.channel, level, fmt, vl); +} + +static void BLog_ChannelContextLog (BLogChannelContext ccontext, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_ChannelContextLogVarArg(ccontext, level, fmt, vl); + va_end(vl); +} + +#endif diff --git a/external/badvpn_dns/base/BLog_syslog.c b/external/badvpn_dns/base/BLog_syslog.c new file mode 100644 index 00000000..d7a954b6 --- /dev/null +++ b/external/badvpn_dns/base/BLog_syslog.c @@ -0,0 +1,150 @@ +/** + * @file BLog_syslog.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include "BLog_syslog.h" + +static int resolve_facility (char *str, int *out) +{ + if (!strcmp(str, "authpriv")) { + *out = LOG_AUTHPRIV; + } + else if (!strcmp(str, "cron")) { + *out = LOG_CRON; + } + else if (!strcmp(str, "daemon")) { + *out = LOG_DAEMON; + } + else if (!strcmp(str, "ftp")) { + *out = LOG_FTP; + } + else if (!strcmp(str, "local0")) { + *out = LOG_LOCAL0; + } + else if (!strcmp(str, "local1")) { + *out = LOG_LOCAL1; + } + else if (!strcmp(str, "local2")) { + *out = LOG_LOCAL2; + } + else if (!strcmp(str, "local3")) { + *out = LOG_LOCAL3; + } + else if (!strcmp(str, "local4")) { + *out = LOG_LOCAL4; + } + else if (!strcmp(str, "local5")) { + *out = LOG_LOCAL5; + } + else if (!strcmp(str, "local6")) { + *out = LOG_LOCAL6; + } + else if (!strcmp(str, "local7")) { + *out = LOG_LOCAL7; + } + else if (!strcmp(str, "lpr")) { + *out = LOG_LPR; + } + else if (!strcmp(str, "mail")) { + *out = LOG_MAIL; + } + else if (!strcmp(str, "news")) { + *out = LOG_NEWS; + } + else if (!strcmp(str, "syslog")) { + *out = LOG_SYSLOG; + } + else if (!strcmp(str, "user")) { + *out = LOG_USER; + } + else if (!strcmp(str, "uucp")) { + *out = LOG_UUCP; + } + else { + return 0; + } + + return 1; +} + +static int convert_level (int level) +{ + ASSERT(level >= BLOG_ERROR && level <= BLOG_DEBUG) + + switch (level) { + case BLOG_ERROR: + return LOG_ERR; + case BLOG_WARNING: + return LOG_WARNING; + case BLOG_NOTICE: + return LOG_NOTICE; + case BLOG_INFO: + return LOG_INFO; + case BLOG_DEBUG: + return LOG_DEBUG; + default: + ASSERT(0) + return 0; + } +} + +static struct { + char ident[200]; +} syslog_global; + +static void syslog_log (int channel, int level, const char *msg) +{ + syslog(convert_level(level), "%s: %s", blog_global.channels[channel].name, msg); +} + +static void syslog_free (void) +{ + closelog(); +} + +int BLog_InitSyslog (char *ident, char *facility_str) +{ + int facility; + if (!resolve_facility(facility_str, &facility)) { + return 0; + } + + snprintf(syslog_global.ident, sizeof(syslog_global.ident), "%s", ident); + + openlog(syslog_global.ident, 0, facility); + + BLog_Init(syslog_log, syslog_free); + + return 1; +} diff --git a/external/badvpn_dns/base/BLog_syslog.h b/external/badvpn_dns/base/BLog_syslog.h new file mode 100644 index 00000000..1adf04ef --- /dev/null +++ b/external/badvpn_dns/base/BLog_syslog.h @@ -0,0 +1,42 @@ +/** + * @file BLog_syslog.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * BLog syslog backend. + */ + +#ifndef BADVPN_BLOG_SYSLOG_H +#define BADVPN_BLOG_SYSLOG_H + +#include +#include + +int BLog_InitSyslog (char *ident, char *facility) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/base/BMutex.h b/external/badvpn_dns/base/BMutex.h new file mode 100644 index 00000000..fbcbd054 --- /dev/null +++ b/external/badvpn_dns/base/BMutex.h @@ -0,0 +1,101 @@ +/** + * @file BMutex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BMUTEX_H +#define BADVPN_BMUTEX_H + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +typedef struct { +#if BADVPN_THREAD_SAFE + pthread_mutex_t pthread_mutex; +#endif + DebugObject d_obj; +} BMutex; + +static int BMutex_Init (BMutex *o) WARN_UNUSED; +static void BMutex_Free (BMutex *o); +static void BMutex_Lock (BMutex *o); +static void BMutex_Unlock (BMutex *o); + +static int BMutex_Init (BMutex *o) +{ +#if BADVPN_THREAD_SAFE + if (pthread_mutex_init(&o->pthread_mutex, NULL) != 0) { + return 0; + } +#endif + + DebugObject_Init(&o->d_obj); + return 1; +} + +static void BMutex_Free (BMutex *o) +{ + DebugObject_Free(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_destroy(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Lock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_lock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +static void BMutex_Unlock (BMutex *o) +{ + DebugObject_Access(&o->d_obj); + +#if BADVPN_THREAD_SAFE + int res = pthread_mutex_unlock(&o->pthread_mutex); + B_USE(res) + ASSERT(res == 0) +#endif +} + +#endif diff --git a/external/badvpn_dns/base/BPending.c b/external/badvpn_dns/base/BPending.c new file mode 100644 index 00000000..6711604a --- /dev/null +++ b/external/badvpn_dns/base/BPending.c @@ -0,0 +1,205 @@ +/** + * @file BPending.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "BPending.h" + +#include "BPending_list.h" +#include + +void BPendingGroup_Init (BPendingGroup *g) +{ + // init jobs list + BPending__List_Init(&g->jobs); + + // init pending counter + DebugCounter_Init(&g->pending_ctr); + + // init debug object + DebugObject_Init(&g->d_obj); +} + +void BPendingGroup_Free (BPendingGroup *g) +{ + DebugCounter_Free(&g->pending_ctr); + ASSERT(BPending__List_IsEmpty(&g->jobs)) + DebugObject_Free(&g->d_obj); +} + +int BPendingGroup_HasJobs (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return !BPending__List_IsEmpty(&g->jobs); +} + +void BPendingGroup_ExecuteJob (BPendingGroup *g) +{ + ASSERT(!BPending__List_IsEmpty(&g->jobs)) + DebugObject_Access(&g->d_obj); + + // get a job + BSmallPending *p = BPending__List_First(&g->jobs); + ASSERT(!BPending__ListIsRemoved(p)) + ASSERT(p->pending) + + // remove from jobs list + BPending__List_RemoveFirst(&g->jobs); + + // set not pending + BPending__ListMarkRemoved(p); +#ifndef NDEBUG + p->pending = 0; +#endif + + // execute job + p->handler(p->user); + return; +} + +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g) +{ + DebugObject_Access(&g->d_obj); + + return BPending__List_First(&g->jobs); +} + +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user) +{ + // init arguments + o->handler = handler; + o->user = user; + + // set not pending + BPending__ListMarkRemoved(o); +#ifndef NDEBUG + o->pending = 0; +#endif + + // increment pending counter + DebugCounter_Increment(&g->pending_ctr); + + // init debug object + DebugObject_Init(&o->d_obj); +} + +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g) +{ + DebugCounter_Decrement(&g->pending_ctr); + DebugObject_Free(&o->d_obj); + ASSERT(o->pending == !BPending__ListIsRemoved(o)) + + // remove from jobs list + if (!BPending__ListIsRemoved(o)) { + BPending__List_Remove(&g->jobs, o); + } +} + +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + // set handler + o->handler = handler; + o->user = user; +} + +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == !BPending__ListIsRemoved(o)) + + // remove from jobs list + if (!BPending__ListIsRemoved(o)) { + BPending__List_Remove(&g->jobs, o); + } + + // insert to jobs list + BPending__List_Prepend(&g->jobs, o); + + // set pending +#ifndef NDEBUG + o->pending = 1; +#endif +} + +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == !BPending__ListIsRemoved(o)) + + if (!BPending__ListIsRemoved(o)) { + // remove from jobs list + BPending__List_Remove(&g->jobs, o); + + // set not pending + BPending__ListMarkRemoved(o); +#ifndef NDEBUG + o->pending = 0; +#endif + } +} + +int BSmallPending_IsSet (BSmallPending *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pending == !BPending__ListIsRemoved(o)) + + return !BPending__ListIsRemoved(o); +} + +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user) +{ + BSmallPending_Init(&o->base, g, handler, user); + o->g = g; +} + +void BPending_Free (BPending *o) +{ + BSmallPending_Free(&o->base, o->g); +} + +void BPending_Set (BPending *o) +{ + BSmallPending_Set(&o->base, o->g); +} + +void BPending_Unset (BPending *o) +{ + BSmallPending_Unset(&o->base, o->g); +} + +int BPending_IsSet (BPending *o) +{ + return BSmallPending_IsSet(&o->base); +} diff --git a/external/badvpn_dns/base/BPending.h b/external/badvpn_dns/base/BPending.h new file mode 100644 index 00000000..07644bea --- /dev/null +++ b/external/badvpn_dns/base/BPending.h @@ -0,0 +1,250 @@ +/** + * @file BPending.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for managing a queue of jobs pending execution. + */ + +#ifndef BADVPN_BPENDING_H +#define BADVPN_BPENDING_H + +#include + +#include +#include +#include + +struct BSmallPending_s; + +#include "BPending_list.h" +#include + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BSmallPending} object was + * in set state. + * The {@link BSmallPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BSmallPending_Init} + */ +typedef void (*BSmallPending_handler) (void *user); + +/** + * Job execution handler. + * It is guaranteed that the associated {@link BPending} object was + * in set state. + * The {@link BPending} object enters not set state before the handler + * is called. + * + * @param user as in {@link BPending_Init} + */ +typedef void (*BPending_handler) (void *user); + +/** + * Object that contains a list of jobs pending execution. + */ +typedef struct { + BPending__List jobs; + DebugCounter pending_ctr; + DebugObject d_obj; +} BPendingGroup; + +/** + * Object for queuing a job for execution. + */ +typedef struct BSmallPending_s { + BPending_handler handler; + void *user; + BPending__ListNode pending_node; // optimization: if not pending, .next is this +#ifndef NDEBUG + uint8_t pending; +#endif + DebugObject d_obj; +} BSmallPending; + +/** + * Object for queuing a job for execution. This is a convenience wrapper + * around {@link BSmallPending} with an extra field to remember the + * {@link BPendingGroup} being used. + */ +typedef struct { + BSmallPending base; + BPendingGroup *g; +} BPending; + +/** + * Initializes the object. + * + * @param g the object + */ +void BPendingGroup_Init (BPendingGroup *g); + +/** + * Frees the object. + * There must be no {@link BPending} or {@link BSmallPending} objects using + * this group. + * + * @param g the object + */ +void BPendingGroup_Free (BPendingGroup *g); + +/** + * Checks if there is at least one job in the queue. + * + * @param g the object + * @return 1 if there is at least one job, 0 if not + */ +int BPendingGroup_HasJobs (BPendingGroup *g); + +/** + * Executes the top job on the job list. + * The job is removed from the list and enters + * not set state before being executed. + * There must be at least one job in job list. + * + * @param g the object + */ +void BPendingGroup_ExecuteJob (BPendingGroup *g); + +/** + * Returns the top job on the job list, or NULL if there are none. + * + * @param g the object + * @return the top job if there is at least one job, NULL if not + */ +BSmallPending * BPendingGroup_PeekJob (BPendingGroup *g); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_Init (BSmallPending *o, BPendingGroup *g, BSmallPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Free (BSmallPending *o, BPendingGroup *g); + +/** + * Changes the job execution handler. + * + * @param o the object + * @param handler job execution handler + * @param user value to pass to handler + */ +void BSmallPending_SetHandler (BSmallPending *o, BSmallPending_handler handler, void *user); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Set (BSmallPending *o, BPendingGroup *g); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + * @param g pending group. Must be the same as was used in {@link BSmallPending_Init}. + */ +void BSmallPending_Unset (BSmallPending *o, BPendingGroup *g); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BSmallPending_IsSet (BSmallPending *o); + +/** + * Initializes the object. + * The object is initialized in not set state. + * + * @param o the object + * @param g pending group to use + * @param handler job execution handler + * @param user value to pass to handler + */ +void BPending_Init (BPending *o, BPendingGroup *g, BPending_handler handler, void *user); + +/** + * Frees the object. + * The execution handler will not be called after the object + * is freed. + * + * @param o the object + */ +void BPending_Free (BPending *o); + +/** + * Enables the job, pushing it to the top of the job list. + * If the object was already in set state, the job is removed from its + * current position in the list before being pushed. + * The object enters set state. + * + * @param o the object + */ +void BPending_Set (BPending *o); + +/** + * Disables the job, removing it from the job list. + * If the object was not in set state, nothing is done. + * The object enters not set state. + * + * @param o the object + */ +void BPending_Unset (BPending *o); + +/** + * Checks if the job is in set state. + * + * @param o the object + * @return 1 if in set state, 0 if not + */ +int BPending_IsSet (BPending *o); + +#endif diff --git a/external/badvpn_dns/base/BPending_list.h b/external/badvpn_dns/base/BPending_list.h new file mode 100644 index 00000000..eadac61a --- /dev/null +++ b/external/badvpn_dns/base/BPending_list.h @@ -0,0 +1,4 @@ +#define SLINKEDLIST_PARAM_NAME BPending__List +#define SLINKEDLIST_PARAM_FEATURE_LAST 0 +#define SLINKEDLIST_PARAM_TYPE_ENTRY struct BSmallPending_s +#define SLINKEDLIST_PARAM_MEMBER_NODE pending_node diff --git a/external/badvpn_dns/base/CMakeLists.txt b/external/badvpn_dns/base/CMakeLists.txt new file mode 100644 index 00000000..cf1f0f0f --- /dev/null +++ b/external/badvpn_dns/base/CMakeLists.txt @@ -0,0 +1,13 @@ +set(BASE_ADDITIONAL_SOURCES) + +if (HAVE_SYSLOG_H) + list(APPEND BASE_ADDITIONAL_SOURCES BLog_syslog.c) +endif () + +set(BASE_SOURCES + DebugObject.c + BLog.c + BPending.c + ${BASE_ADDITIONAL_SOURCES} +) +badvpn_add_library(base "" "" "${BASE_SOURCES}") diff --git a/external/badvpn_dns/base/DebugObject.c b/external/badvpn_dns/base/DebugObject.c new file mode 100644 index 00000000..e6946177 --- /dev/null +++ b/external/badvpn_dns/base/DebugObject.c @@ -0,0 +1,39 @@ +/** + * @file DebugObject.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "DebugObject.h" + +#ifndef BADVPN_PLUGIN +#ifndef NDEBUG +DebugCounter debugobject_counter = DEBUGCOUNTER_STATIC; +#if BADVPN_THREAD_SAFE +pthread_mutex_t debugobject_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +#endif +#endif diff --git a/external/badvpn_dns/base/DebugObject.h b/external/badvpn_dns/base/DebugObject.h new file mode 100644 index 00000000..b8db287e --- /dev/null +++ b/external/badvpn_dns/base/DebugObject.h @@ -0,0 +1,147 @@ +/** + * @file DebugObject.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used for detecting leaks. + */ + +#ifndef BADVPN_DEBUGOBJECT_H +#define BADVPN_DEBUGOBJECT_H + +#include + +#if !defined(BADVPN_THREAD_SAFE) || (BADVPN_THREAD_SAFE != 0 && BADVPN_THREAD_SAFE != 1) +#error BADVPN_THREAD_SAFE is not defined or incorrect +#endif + +#if BADVPN_THREAD_SAFE +#include +#endif + +#include +#include + +#define DEBUGOBJECT_VALID UINT32_C(0x31415926) + +/** + * Object used for detecting leaks. + */ +typedef struct { + #ifndef NDEBUG + uint32_t c; + #endif +} DebugObject; + +/** + * Initializes the object. + * + * @param obj the object + */ +static void DebugObject_Init (DebugObject *obj); + +/** + * Frees the object. + * + * @param obj the object + */ +static void DebugObject_Free (DebugObject *obj); + +/** + * Does nothing. + * + * @param obj the object + */ +static void DebugObject_Access (const DebugObject *obj); + +/** + * Does nothing. + * There must be no {@link DebugObject}'s initialized. + */ +static void DebugObjectGlobal_Finish (void); + +#ifndef NDEBUG +extern DebugCounter debugobject_counter; +#if BADVPN_THREAD_SAFE +extern pthread_mutex_t debugobject_mutex; +#endif +#endif + +void DebugObject_Init (DebugObject *obj) +{ + #ifndef NDEBUG + + obj->c = DEBUGOBJECT_VALID; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Increment(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Free (DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) + + #ifndef NDEBUG + + obj->c = 0; + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_lock(&debugobject_mutex) == 0) + #endif + + DebugCounter_Decrement(&debugobject_counter); + + #if BADVPN_THREAD_SAFE + ASSERT_FORCE(pthread_mutex_unlock(&debugobject_mutex) == 0) + #endif + + #endif +} + +void DebugObject_Access (const DebugObject *obj) +{ + ASSERT(obj->c == DEBUGOBJECT_VALID) +} + +void DebugObjectGlobal_Finish (void) +{ + #ifndef NDEBUG + DebugCounter_Free(&debugobject_counter); + #endif +} + +#endif diff --git a/external/badvpn_dns/blog_channels.txt b/external/badvpn_dns/blog_channels.txt new file mode 100644 index 00000000..96313b54 --- /dev/null +++ b/external/badvpn_dns/blog_channels.txt @@ -0,0 +1,145 @@ +server 4 +client 4 +flooder 4 +tun2socks 4 +ncd 4 +ncd_var 4 +ncd_list 4 +ncd_depend 4 +ncd_multidepend 4 +ncd_dynamic_depend 4 +ncd_concat 4 +ncd_if 4 +ncd_strcmp 4 +ncd_regex_match 4 +ncd_logical 4 +ncd_sleep 4 +ncd_print 4 +ncd_blocker 4 +ncd_run 4 +ncd_runonce 4 +ncd_daemon 4 +ncd_spawn 4 +ncd_imperative 4 +ncd_ref 4 +ncd_index 4 +ncd_alias 4 +ncd_process_manager 4 +ncd_ondemand 4 +ncd_foreach 4 +ncd_choose 4 +ncd_net_backend_waitdevice 4 +ncd_net_backend_waitlink 4 +ncd_net_backend_badvpn 4 +ncd_net_backend_wpa_supplicant 4 +ncd_net_backend_rfkill 4 +ncd_net_up 4 +ncd_net_dns 4 +ncd_net_iptables 4 +ncd_net_ipv4_addr 4 +ncd_net_ipv4_route 4 +ncd_net_ipv4_dhcp 4 +ncd_net_ipv4_arp_probe 4 +ncd_net_watch_interfaces 4 +ncd_sys_watch_input 4 +ncd_sys_watch_usb 4 +ncd_sys_evdev 4 +ncd_sys_watch_directory 4 +StreamPeerIO 4 +DatagramPeerIO 4 +BReactor 3 +BSignal 3 +FragmentProtoAssembler 4 +BPredicate 3 +ServerConnection 4 +Listener 4 +DataProto 4 +FrameDecider 4 +BSocksClient 4 +BDHCPClientCore 4 +BDHCPClient 4 +NCDIfConfig 4 +BUnixSignal 4 +BProcess 4 +PRStreamSink 4 +PRStreamSource 4 +PacketProtoDecoder 4 +DPRelay 4 +BThreadWork 4 +DPReceive 4 +BInputProcess 4 +NCDUdevMonitorParser 4 +NCDUdevMonitor 4 +NCDUdevCache 4 +NCDUdevManager 4 +BTime 4 +BEncryption 4 +SPProtoDecoder 4 +LineBuffer 4 +BTap 4 +lwip 4 +NCDConfigTokenizer 4 +NCDConfigParser 4 +NCDValParser 4 +nsskey 4 +addr 4 +PasswordListener 4 +NCDInterfaceMonitor 4 +NCDRfkillMonitor 4 +udpgw 4 +UdpGwClient 4 +SocksUdpGwClient 4 +BNetwork 4 +BConnection 4 +BSSLConnection 4 +BDatagram 4 +PeerChat 4 +BArpProbe 4 +NCDModuleIndex 4 +NCDModuleProcess 4 +NCDValGenerator 4 +ncd_from_string 4 +ncd_to_string 4 +ncd_value 4 +ncd_try 4 +ncd_sys_request_server 4 +NCDRequest 4 +ncd_net_ipv6_wait_dynamic_addr 4 +NCDRequestClient 4 +ncd_request 4 +ncd_sys_request_client 4 +ncd_exit 4 +ncd_getargs 4 +ncd_arithmetic 4 +ncd_parse 4 +ncd_valuemetic 4 +ncd_file 4 +ncd_netmask 4 +ncd_implode 4 +ncd_call2 4 +ncd_assert 4 +ncd_reboot 4 +ncd_explode 4 +NCDPlaceholderDb 4 +NCDVal 4 +ncd_net_ipv6_addr 4 +ncd_net_ipv6_route 4 +ncd_net_ipv4_addr_in_network 4 +ncd_net_ipv6_addr_in_network 4 +dostest_server 4 +dostest_attacker 4 +ncd_timer 4 +ncd_file_open 4 +ncd_backtrack 4 +ncd_socket 4 +ncd_depend_scope 4 +ncd_substr 4 +ncd_sys_start_process 4 +NCDBuildProgram 4 +ncd_log 4 +ncd_log_msg 4 +ncd_buffer 4 +ncd_getenv 4 +BThreadSignal 4 +BLockReactor 4 +ncd_load_module 4 diff --git a/external/badvpn_dns/blog_generator/blog.php b/external/badvpn_dns/blog_generator/blog.php new file mode 100644 index 00000000..fd436bc8 --- /dev/null +++ b/external/badvpn_dns/blog_generator/blog.php @@ -0,0 +1,121 @@ + Input channels file. + --output-dir

Destination directory for generated files. + +EOD; +} + +$input_file = ""; +$output_dir = ""; + +for ($i = 1; $i < $argc;) { + $arg = $argv[$i++]; + switch ($arg) { + case "--input-file": + $input_file = $argv[$i++]; + break; + case "--output-dir": + $output_dir = $argv[$i++]; + break; + case "--help": + print_help($argv[0]); + exit(0); + default: + fatal_error("Unknown option: {$arg}"); + } +} + +if ($input_file == "") { + fatal_error("--input-file missing"); +} + +if ($output_dir == "") { + fatal_error("--output-dir missing"); +} + +if (($data = file_get_contents($input_file)) === FALSE) { + fatal_error("Failed to read input file"); +} + +if (!tokenize($data, $tokens)) { + fatal_error("Failed to tokenize"); +} + +$i = 0; +$channels_defines = ""; +$channels_list = ""; + +reset($tokens); + +while (1) { + if (($ch_name = current($tokens)) === FALSE) { + break; + } + next($tokens); + if (($ch_priority = current($tokens)) === FALSE) { + fatal_error("missing priority"); + } + next($tokens); + if ($ch_name[0] != "name") { + fatal_error("name is not a name"); + } + if ($ch_priority[0] != "number") { + fatal_error("priority is not a number"); + } + + $channel_file = << 0) { + if (preg_match('/^\\/\\/.*/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\\s+/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[0-9]+/', $str, $matches)) { + $out[] = array('number', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) { + $out[] = array('name', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else { + return FALSE; + } + } + + return TRUE; +} + +function fatal_error ($message) +{ + fwrite(STDERR, "Fatal error: $message\n"); + + ob_get_clean(); + exit(1); +} diff --git a/external/badvpn_dns/bproto/BProto.h b/external/badvpn_dns/bproto/BProto.h new file mode 100644 index 00000000..5f2a6969 --- /dev/null +++ b/external/badvpn_dns/bproto/BProto.h @@ -0,0 +1,85 @@ +/** + * @file BProto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for BProto serialization. + */ + +#ifndef BADVPN_BPROTO_BPROTO_H +#define BADVPN_BPROTO_BPROTO_H + +#include + +#include + +#define BPROTO_TYPE_UINT8 1 +#define BPROTO_TYPE_UINT16 2 +#define BPROTO_TYPE_UINT32 3 +#define BPROTO_TYPE_UINT64 4 +#define BPROTO_TYPE_DATA 5 +#define BPROTO_TYPE_CONSTDATA 6 + +B_START_PACKED +struct BProto_header_s { + uint16_t id; + uint16_t type; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint8_s { + uint8_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint16_s { + uint16_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint32_s { + uint32_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_uint64_s { + uint64_t v; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct BProto_data_header_s { + uint32_t len; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/bproto_generator/ProtoParser.lime b/external/badvpn_dns/bproto_generator/ProtoParser.lime new file mode 100644 index 00000000..f039e1d3 --- /dev/null +++ b/external/badvpn_dns/bproto_generator/ProtoParser.lime @@ -0,0 +1,99 @@ +%class ProtoParser +%start file + +file = + directives messages { + $$ = array( + "directives" => $1, + "messages" => $2 + ); + }. + +directives = + { + $$ = array(); + } | + directive semicolon directives { + $$ = array_merge(array($1), $3); + }. + +directive = + include string { + $$ = array( + "type" => "include", + "file" => $2 + ); + }. + +messages = + msgspec { + $$ = array($1); + } | + msgspec messages { + $$ = array_merge(array($1), $2); + }. + +msgspec = + message name spar entries epar semicolon { + $$ = array( + "name" => $2, + "entries" => $4 + ); +}. + +entries = + entry { + $$ = array($1); + } | + entry entries { + $$ = array_merge(array($1), $2); + }. + +entry = + cardinality type name equals number semicolon { + $$ = array( + "cardinality" => $1, + "type" => $2, + "name" => $3, + "id" => $5 + ); + }. + +cardinality = + repeated { + $$ = "repeated"; + } | + optional { + $$ = "optional"; + } | + required { + $$ = "required"; + } | + required repeated { + $$ = "required repeated"; + }. + +type = + uint { + $$ = array( + "type" => "uint", + "size" => $1 + ); + } | + data { + $$ = array( + "type" => "data" + ); + } | + data srpar string erpar { + $$ = array( + "type" => "constdata", + "size" => $3 + ); + } | + message name { + $$ = array( + "type" => "message", + "message" => $2 + ); + }. diff --git a/external/badvpn_dns/bproto_generator/ProtoParser.php b/external/badvpn_dns/bproto_generator/ProtoParser.php new file mode 100644 index 00000000..0477dbaf --- /dev/null +++ b/external/badvpn_dns/bproto_generator/ProtoParser.php @@ -0,0 +1,560 @@ + + array ( + 'directives' => 's 1', + 'directive' => 's 30', + 'include' => 's 33', + 'file' => 's 35', + '\'start\'' => 'a \'start\'', + 'message' => 'r 1', + ), + 1 => + array ( + 'messages' => 's 2', + 'msgspec' => 's 3', + 'message' => 's 5', + ), + 2 => + array ( + '#' => 'r 0', + ), + 3 => + array ( + 'msgspec' => 's 3', + 'messages' => 's 4', + 'message' => 's 5', + '#' => 'r 4', + ), + 4 => + array ( + '#' => 'r 5', + ), + 5 => + array ( + 'name' => 's 6', + ), + 6 => + array ( + 'spar' => 's 7', + ), + 7 => + array ( + 'entries' => 's 8', + 'entry' => 's 11', + 'cardinality' => 's 13', + 'repeated' => 's 26', + 'optional' => 's 27', + 'required' => 's 28', + ), + 8 => + array ( + 'epar' => 's 9', + ), + 9 => + array ( + 'semicolon' => 's 10', + ), + 10 => + array ( + 'message' => 'r 6', + '#' => 'r 6', + ), + 11 => + array ( + 'entry' => 's 11', + 'entries' => 's 12', + 'cardinality' => 's 13', + 'repeated' => 's 26', + 'optional' => 's 27', + 'required' => 's 28', + 'epar' => 'r 7', + ), + 12 => + array ( + 'epar' => 'r 8', + ), + 13 => + array ( + 'type' => 's 14', + 'uint' => 's 19', + 'data' => 's 20', + 'message' => 's 24', + ), + 14 => + array ( + 'name' => 's 15', + ), + 15 => + array ( + 'equals' => 's 16', + ), + 16 => + array ( + 'number' => 's 17', + ), + 17 => + array ( + 'semicolon' => 's 18', + ), + 18 => + array ( + 'repeated' => 'r 9', + 'optional' => 'r 9', + 'required' => 'r 9', + 'epar' => 'r 9', + ), + 19 => + array ( + 'name' => 'r 14', + ), + 20 => + array ( + 'srpar' => 's 21', + 'name' => 'r 15', + ), + 21 => + array ( + 'string' => 's 22', + ), + 22 => + array ( + 'erpar' => 's 23', + ), + 23 => + array ( + 'name' => 'r 16', + ), + 24 => + array ( + 'name' => 's 25', + ), + 25 => + array ( + 'name' => 'r 17', + ), + 26 => + array ( + 'uint' => 'r 10', + 'data' => 'r 10', + 'message' => 'r 10', + ), + 27 => + array ( + 'uint' => 'r 11', + 'data' => 'r 11', + 'message' => 'r 11', + ), + 28 => + array ( + 'repeated' => 's 29', + 'uint' => 'r 12', + 'data' => 'r 12', + 'message' => 'r 12', + ), + 29 => + array ( + 'uint' => 'r 13', + 'data' => 'r 13', + 'message' => 'r 13', + ), + 30 => + array ( + 'semicolon' => 's 31', + ), + 31 => + array ( + 'directive' => 's 30', + 'directives' => 's 32', + 'include' => 's 33', + 'message' => 'r 1', + ), + 32 => + array ( + 'message' => 'r 2', + ), + 33 => + array ( + 'string' => 's 34', + ), + 34 => + array ( + 'semicolon' => 'r 3', + ), + 35 => + array ( + '#' => 'r 18', + ), +); +function reduce_0_file_1($tokens, &$result) { +# +# (0) file := directives messages +# +$result = reset($tokens); + + $result = array( + "directives" => $tokens[0], + "messages" => $tokens[1] + ); + +} + +function reduce_1_directives_1($tokens, &$result) { +# +# (1) directives := +# +$result = reset($tokens); + + $result = array(); + +} + +function reduce_2_directives_2($tokens, &$result) { +# +# (2) directives := directive semicolon directives +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[2]); + +} + +function reduce_3_directive_1($tokens, &$result) { +# +# (3) directive := include string +# +$result = reset($tokens); + + $result = array( + "type" => "include", + "file" => $tokens[1] + ); + +} + +function reduce_4_messages_1($tokens, &$result) { +# +# (4) messages := msgspec +# +$result = reset($tokens); + + $result = array($tokens[0]); + +} + +function reduce_5_messages_2($tokens, &$result) { +# +# (5) messages := msgspec messages +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[1]); + +} + +function reduce_6_msgspec_1($tokens, &$result) { +# +# (6) msgspec := message name spar entries epar semicolon +# +$result = reset($tokens); + + $result = array( + "name" => $tokens[1], + "entries" => $tokens[3] + ); + +} + +function reduce_7_entries_1($tokens, &$result) { +# +# (7) entries := entry +# +$result = reset($tokens); + + $result = array($tokens[0]); + +} + +function reduce_8_entries_2($tokens, &$result) { +# +# (8) entries := entry entries +# +$result = reset($tokens); + + $result = array_merge(array($tokens[0]), $tokens[1]); + +} + +function reduce_9_entry_1($tokens, &$result) { +# +# (9) entry := cardinality type name equals number semicolon +# +$result = reset($tokens); + + $result = array( + "cardinality" => $tokens[0], + "type" => $tokens[1], + "name" => $tokens[2], + "id" => $tokens[4] + ); + +} + +function reduce_10_cardinality_1($tokens, &$result) { +# +# (10) cardinality := repeated +# +$result = reset($tokens); + + $result = "repeated"; + +} + +function reduce_11_cardinality_2($tokens, &$result) { +# +# (11) cardinality := optional +# +$result = reset($tokens); + + $result = "optional"; + +} + +function reduce_12_cardinality_3($tokens, &$result) { +# +# (12) cardinality := required +# +$result = reset($tokens); + + $result = "required"; + +} + +function reduce_13_cardinality_4($tokens, &$result) { +# +# (13) cardinality := required repeated +# +$result = reset($tokens); + + $result = "required repeated"; + +} + +function reduce_14_type_1($tokens, &$result) { +# +# (14) type := uint +# +$result = reset($tokens); + + $result = array( + "type" => "uint", + "size" => $tokens[0] + ); + +} + +function reduce_15_type_2($tokens, &$result) { +# +# (15) type := data +# +$result = reset($tokens); + + $result = array( + "type" => "data" + ); + +} + +function reduce_16_type_3($tokens, &$result) { +# +# (16) type := data srpar string erpar +# +$result = reset($tokens); + + $result = array( + "type" => "constdata", + "size" => $tokens[2] + ); + +} + +function reduce_17_type_4($tokens, &$result) { +# +# (17) type := message name +# +$result = reset($tokens); + + $result = array( + "type" => "message", + "message" => $tokens[1] + ); + +} + +function reduce_18_start_1($tokens, &$result) { +# +# (18) 'start' := file +# +$result = reset($tokens); + +} + +var $method = array ( + 0 => 'reduce_0_file_1', + 1 => 'reduce_1_directives_1', + 2 => 'reduce_2_directives_2', + 3 => 'reduce_3_directive_1', + 4 => 'reduce_4_messages_1', + 5 => 'reduce_5_messages_2', + 6 => 'reduce_6_msgspec_1', + 7 => 'reduce_7_entries_1', + 8 => 'reduce_8_entries_2', + 9 => 'reduce_9_entry_1', + 10 => 'reduce_10_cardinality_1', + 11 => 'reduce_11_cardinality_2', + 12 => 'reduce_12_cardinality_3', + 13 => 'reduce_13_cardinality_4', + 14 => 'reduce_14_type_1', + 15 => 'reduce_15_type_2', + 16 => 'reduce_16_type_3', + 17 => 'reduce_17_type_4', + 18 => 'reduce_18_start_1', +); +var $a = array ( + 0 => + array ( + 'symbol' => 'file', + 'len' => 2, + 'replace' => true, + ), + 1 => + array ( + 'symbol' => 'directives', + 'len' => 0, + 'replace' => true, + ), + 2 => + array ( + 'symbol' => 'directives', + 'len' => 3, + 'replace' => true, + ), + 3 => + array ( + 'symbol' => 'directive', + 'len' => 2, + 'replace' => true, + ), + 4 => + array ( + 'symbol' => 'messages', + 'len' => 1, + 'replace' => true, + ), + 5 => + array ( + 'symbol' => 'messages', + 'len' => 2, + 'replace' => true, + ), + 6 => + array ( + 'symbol' => 'msgspec', + 'len' => 6, + 'replace' => true, + ), + 7 => + array ( + 'symbol' => 'entries', + 'len' => 1, + 'replace' => true, + ), + 8 => + array ( + 'symbol' => 'entries', + 'len' => 2, + 'replace' => true, + ), + 9 => + array ( + 'symbol' => 'entry', + 'len' => 6, + 'replace' => true, + ), + 10 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 11 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 12 => + array ( + 'symbol' => 'cardinality', + 'len' => 1, + 'replace' => true, + ), + 13 => + array ( + 'symbol' => 'cardinality', + 'len' => 2, + 'replace' => true, + ), + 14 => + array ( + 'symbol' => 'type', + 'len' => 1, + 'replace' => true, + ), + 15 => + array ( + 'symbol' => 'type', + 'len' => 1, + 'replace' => true, + ), + 16 => + array ( + 'symbol' => 'type', + 'len' => 4, + 'replace' => true, + ), + 17 => + array ( + 'symbol' => 'type', + 'len' => 2, + 'replace' => true, + ), + 18 => + array ( + 'symbol' => '\'start\'', + 'len' => 1, + 'replace' => true, + ), +); +} diff --git a/external/badvpn_dns/bproto_generator/bproto.php b/external/badvpn_dns/bproto_generator/bproto.php new file mode 100644 index 00000000..76f8c6e4 --- /dev/null +++ b/external/badvpn_dns/bproto_generator/bproto.php @@ -0,0 +1,115 @@ + + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +require_once "lime/parse_engine.php"; +require_once "ProtoParser.php"; +require_once "bproto_functions.php"; + +function assert_failure ($script, $line, $message) +{ + if ($message == "") { + fatal_error("Assertion failure at {$script}:{$line}"); + } else { + fatal_error("Assertion failure at {$script}:{$line}: {$message}"); + } +} + +assert_options(ASSERT_CALLBACK, "assert_failure"); + +function print_help ($name) +{ + echo << Output file prefix. + --input-file Message file to generate source for. + --output-dir Destination directory for generated files. + +EOD; +} + +$name = ""; +$input_file = ""; +$output_dir = ""; + +for ($i = 1; $i < $argc;) { + $arg = $argv[$i++]; + switch ($arg) { + case "--name": + $name = $argv[$i++]; + break; + case "--input-file": + $input_file = $argv[$i++]; + break; + case "--output-dir": + $output_dir = $argv[$i++]; + break; + case "--help": + print_help($argv[0]); + exit(0); + default: + fatal_error("Unknown option: {$arg}"); + } +} + +if ($name == "") { + fatal_error("--name missing"); +} + +if ($input_file == "") { + fatal_error("--input-file missing"); +} + +if ($output_dir == "") { + fatal_error("--output-dir missing"); +} + +if (($data = file_get_contents($input_file)) === FALSE) { + fatal_error("Failed to read input file"); +} + +if (!tokenize($data, $tokens)) { + fatal_error("Failed to tokenize"); +} + +$parser = new parse_engine(new ProtoParser()); + +try { + foreach ($tokens as $token) { + $parser->eat($token[0], $token[1]); + } + $parser->eat_eof(); +} catch (parse_error $e) { + fatal_error("$input_file: Parse error: ".$e->getMessage()); +} + +$data = generate_header($name, $parser->semantic["directives"], $parser->semantic["messages"]); +if (file_put_contents("{$output_dir}/{$name}.h", $data) === NULL) { + fatal_error("{$input_file}: Failed to write .h file"); +} diff --git a/external/badvpn_dns/bproto_generator/bproto_functions.php b/external/badvpn_dns/bproto_generator/bproto_functions.php new file mode 100644 index 00000000..490c1bf1 --- /dev/null +++ b/external/badvpn_dns/bproto_generator/bproto_functions.php @@ -0,0 +1,777 @@ + 0) { + if (preg_match('/^\\/\\/.*/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\\s+/', $str, $matches)) { + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^include/', $str, $matches)) { + $out[] = array('include', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^message/', $str, $matches)) { + $out[] = array('message', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^repeated/', $str, $matches)) { + $out[] = array('repeated', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^required/', $str, $matches)) { + $out[] = array('required', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^optional/', $str, $matches)) { + $out[] = array('optional', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^{/', $str, $matches)) { + $out[] = array('spar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^}/', $str, $matches)) { + $out[] = array('epar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\(/', $str, $matches)) { + $out[] = array('srpar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^\)/', $str, $matches)) { + $out[] = array('erpar', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^=/', $str, $matches)) { + $out[] = array('equals', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^;/', $str, $matches)) { + $out[] = array('semicolon', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^uint(8|16|32|64)/', $str, $matches)) { + $out[] = array('uint', $matches[1]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^data/', $str, $matches)) { + $out[] = array('data', null); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[0-9]+/', $str, $matches)) { + $out[] = array('number', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*/', $str, $matches)) { + $out[] = array('name', $matches[0]); + $str = substr($str, strlen($matches[0])); + } + else if (preg_match('/^"([^"]*)"/', $str, $matches)) { + $out[] = array('string', $matches[1]); + $str = substr($str, strlen($matches[0])); + } + else { + return FALSE; + } + } + + return TRUE; +} + +function fatal_error ($message) +{ + fwrite(STDERR, "Fatal error: $message\n"); + + ob_get_clean(); + exit(1); +} + +function make_writer_decl ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "void {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, uint{$entry["type"]["size"]}_t v)"; + case "data": + return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o, int len)"; + case "constdata": + return "uint8_t * {$msg["name"]}Writer_Add{$entry["name"]} ({$msg["name"]}Writer *o)"; + default: + assert(0); + } +} + +function make_parser_decl ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint{$entry["type"]["size"]}_t *v)"; + case "data": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data, int *data_len)"; + case "constdata": + return "int {$msg["name"]}Parser_Get{$entry["name"]} ({$msg["name"]}Parser *o, uint8_t **data)"; + default: + assert(0); + } +} + +function make_parser_reset_decl ($msg, $entry) +{ + return "void {$msg["name"]}Parser_Reset{$entry["name"]} ({$msg["name"]}Parser *o)"; +} + +function make_parser_forward_decl ($msg, $entry) +{ + return "void {$msg["name"]}Parser_Forward{$entry["name"]} ({$msg["name"]}Parser *o)"; +} + +function make_type_name ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "BPROTO_TYPE_UINT{$entry["type"]["size"]}"; + case "data": + return "BPROTO_TYPE_DATA"; + case "constdata": + return "BPROTO_TYPE_CONSTDATA"; + default: + assert(0); + } +} + +function make_finish_assert ($msg, $entry) +{ + switch ($entry["cardinality"]) { + case "repeated": + return "ASSERT(o->{$entry["name"]}_count >= 0)"; + case "required repeated": + return "ASSERT(o->{$entry["name"]}_count >= 1)"; + case "optional": + return "ASSERT(o->{$entry["name"]}_count >= 0 && o->{$entry["name"]}_count <= 1)"; + case "required": + return "ASSERT(o->{$entry["name"]}_count == 1)"; + default: + assert(0); + } +} + +function make_add_count_assert ($msg, $entry) +{ + if (in_array($entry["cardinality"], array("optional", "required"))) { + return "ASSERT(o->{$entry["name"]}_count == 0)"; + } + return ""; +} + +function make_add_length_assert ($msg, $entry) +{ + if ($entry["type"]["type"] == "data") { + return "ASSERT(len >= 0 && len <= UINT32_MAX)"; + } + return ""; +} + +function make_size_define ($msg, $entry) +{ + switch ($entry["type"]["type"]) { + case "uint": + return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint{$entry["type"]["size"]}_s))"; + case "data": + return "#define {$msg["name"]}_SIZE{$entry["name"]}(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len))"; + case "constdata": + return "#define {$msg["name"]}_SIZE{$entry["name"]} (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + ({$entry["type"]["size"]}))"; + default: + assert(0); + } +} + +function generate_header ($name, $directives, $messages) { + ob_start(); + + echo << +#include + +#include +#include +#include + + +EOD; + + foreach ($directives as $directive) { + if ($directive["type"] == "include") { + echo <<out = out; + o->used = 0; + +EOD; + + foreach ($msg["entries"] as $entry) { + echo <<{$entry["name"]}_count = 0; + +EOD; + } + + echo <<used >= 0) + +EOD; + + foreach ($msg["entries"] as $entry) { + $ass = make_finish_assert($msg, $entry); + echo <<used; +} + + +EOD; + + foreach ($msg["entries"] as $entry) { + $decl = make_writer_decl($msg, $entry); + $type = make_type_name($msg, $entry); + $add_count_assert = make_add_count_assert($msg, $entry); + $add_length_assert = make_add_length_assert($msg, $entry); + + echo <<used >= 0) + {$add_count_assert} + {$add_length_assert} + + struct BProto_header_s header; + header.id = htol16({$entry["id"]}); + header.type = htol16({$type}); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + +EOD; + switch ($entry["type"]["type"]) { + case "uint": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint{$entry["type"]["size"]}_s); + +EOD; + break; + case "data": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + +EOD; + break; + case "constdata": + echo <<out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += ({$entry["type"]["size"]}); + +EOD; + break; + default: + assert(0); + } + + echo <<{$entry["name"]}_count++; + +EOD; + if (in_array($entry["type"]["type"], array("data", "constdata"))) { + echo <<= 0) + + o->buf = buf; + o->buf_len = buf_len; + +EOD; + + foreach ($msg["entries"] as $entry) { + echo <<{$entry["name"]}_start = o->buf_len; + o->{$entry["name"]}_span = 0; + o->{$entry["name"]}_pos = 0; + +EOD; + } + + echo <<buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + +EOD; + + foreach (array(8, 16, 32, 64) as $bits) { + echo <<= sizeof(struct BProto_uint{$bits}_s))) { + return 0; + } + pos += sizeof(struct BProto_uint{$bits}_s); + left -= sizeof(struct BProto_uint{$bits}_s); + + switch (id) { + +EOD; + + foreach ($msg["entries"] as $entry) { + if (!($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits)) { + continue; + } + $type = make_type_name($msg, $entry); + echo <<{$entry["name"]}_start == o->buf_len) { + o->{$entry["name"]}_start = entry_pos; + } + o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start; + {$entry["name"]}_count++; + break; + +EOD; + } + + echo <<= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + +EOD; + + foreach ($msg["entries"] as $entry) { + if (!in_array($entry["type"]["type"], array("data", "constdata"))) { + continue; + } + $type = make_type_name($msg, $entry); + echo <<{$entry["name"]}_start == o->buf_len) { + o->{$entry["name"]}_start = entry_pos; + } + o->{$entry["name"]}_span = pos - o->{$entry["name"]}_start; + {$entry["name"]}_count++; + break; + +EOD; + } + + echo <<= 1"; + break; + case "optional": + $cond = "{$entry["name"]}_count <= 1"; + break; + case "required": + $cond = "{$entry["name"]}_count == 1"; + break; + default: + assert(0); + } + if ($cond) { + echo <<{$entry["name"]}_pos == o->{$entry["name"]}_span + +EOD; + } + + + echo <<{$entry["name"]}_pos >= 0) + ASSERT(o->{$entry["name"]}_pos <= o->{$entry["name"]}_span) + + int left = o->{$entry["name"]}_span - o->{$entry["name"]}_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(header)); + o->{$entry["name"]}_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + +EOD; + + foreach (array(8, 16, 32, 64) as $bits) { + echo <<= sizeof(struct BProto_uint{$bits}_s)) + +EOD; + if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) { + echo <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val)); + +EOD; + } + echo <<{$entry["name"]}_pos += sizeof(struct BProto_uint{$bits}_s); + left -= sizeof(struct BProto_uint{$bits}_s); + +EOD; + if ($entry["type"]["type"] == "uint" && $entry["type"]["size"] == $bits) { + echo <<= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos, sizeof(val)); + o->{$entry["name"]}_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + +EOD; + if ($entry["type"]["type"] == "data" || $entry["type"]["type"] == "constdata") { + echo <<buf + o->{$entry["name"]}_start + o->{$entry["name"]}_pos; + +EOD; + } + echo <<{$entry["name"]}_pos += payload_len; + left -= payload_len; + +EOD; + if ($entry["type"]["type"] == "data") { + echo <<{$entry["name"]}_pos = 0; +} + +{$forward_decl} +{ + o->{$entry["name"]}_pos = o->{$entry["name"]}_span; +} + + +EOD; + } + } + + return ob_get_clean(); +} diff --git a/external/badvpn_dns/client/CMakeLists.txt b/external/badvpn_dns/client/CMakeLists.txt new file mode 100644 index 00000000..3cec1a9d --- /dev/null +++ b/external/badvpn_dns/client/CMakeLists.txt @@ -0,0 +1,30 @@ +add_executable(badvpn-client + client.c + StreamPeerIO.c + DatagramPeerIO.c + PasswordListener.c + DataProto.c + FrameDecider.c + DPRelay.c + DPReceive.c + FragmentProtoDisassembler.c + FragmentProtoAssembler.c + SPProtoEncoder.c + SPProtoDecoder.c + DataProtoKeepaliveSource.c + PeerChat.c + SCOutmsgEncoder.c + SimpleStreamBuffer.c + SinglePacketSource.c +) +target_link_libraries(badvpn-client system flow flowextra tuntap server_conection security threadwork ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-client + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-client.8 + DESTINATION share/man/man8 +) diff --git a/external/badvpn_dns/client/DPReceive.c b/external/badvpn_dns/client/DPReceive.c new file mode 100644 index 00000000..da70c745 --- /dev/null +++ b/external/badvpn_dns/client/DPReceive.c @@ -0,0 +1,324 @@ +/** + * @file DPReceive.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static DPReceivePeer * find_peer (DPReceiveDevice *o, peerid_t id) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->peers_list); node; node = LinkedList1Node_Next(node)) { + DPReceivePeer *p = UPPER_OBJECT(node, DPReceivePeer, list_node); + if (p->peer_id == id) { + return p; + } + } + + return NULL; +} + +static void receiver_recv_handler_send (DPReceiveReceiver *o, uint8_t *packet, int packet_len) +{ + DebugObject_Access(&o->d_obj); + DPReceivePeer *peer = o->peer; + DPReceiveDevice *device = peer->device; + ASSERT(packet_len >= 0) + ASSERT(packet_len <= device->packet_mtu) + + uint8_t *data = packet; + int data_len = packet_len; + + int local = 0; + DPReceivePeer *src_peer; + DPReceivePeer *relay_dest_peer = NULL; + + // check header + if (data_len < sizeof(struct dataproto_header)) { + BLog(BLOG_WARNING, "no dataproto header"); + goto out; + } + struct dataproto_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + peerid_t from_id = ltoh16(header.from_id); + int num_ids = ltoh16(header.num_peer_ids); + + // check destination ID + if (!(num_ids == 0 || num_ids == 1)) { + BLog(BLOG_WARNING, "wrong number of destinations"); + goto out; + } + peerid_t to_id = 0; // to remove warning + if (num_ids == 1) { + if (data_len < sizeof(struct dataproto_peer_id)) { + BLog(BLOG_WARNING, "missing destination"); + goto out; + } + struct dataproto_peer_id id; + memcpy(&id, data, sizeof(id)); + to_id = ltoh16(id.id); + data += sizeof(id); + data_len -= sizeof(id); + } + + // check remaining data + if (data_len > device->device_mtu) { + BLog(BLOG_WARNING, "frame too large"); + goto out; + } + + // inform sink of received packet + if (peer->dp_sink) { + DataProtoSink_Received(peer->dp_sink, !!(flags & DATAPROTO_FLAGS_RECEIVING_KEEPALIVES)); + } + + if (num_ids == 1) { + // find source peer + if (!(src_peer = find_peer(device, from_id))) { + BLog(BLOG_INFO, "source peer %d not known", (int)from_id); + goto out; + } + + // is frame for device or another peer? + if (device->have_peer_id && to_id == device->peer_id) { + // let the frame decider analyze the frame + FrameDeciderPeer_Analyze(src_peer->decider_peer, data, data_len); + + // pass frame to device + local = 1; + } else { + // check if relaying is allowed + if (!peer->is_relay_client) { + BLog(BLOG_WARNING, "relaying not allowed"); + goto out; + } + + // provided source ID must be the peer sending the frame + if (src_peer != peer) { + BLog(BLOG_WARNING, "relay source must be the sending peer"); + goto out; + } + + // find destination peer + DPReceivePeer *dest_peer = find_peer(device, to_id); + if (!dest_peer) { + BLog(BLOG_INFO, "relay destination peer not known"); + goto out; + } + + // destination cannot be source + if (dest_peer == src_peer) { + BLog(BLOG_WARNING, "relay destination cannot be the source"); + goto out; + } + + relay_dest_peer = dest_peer; + } + } + +out: + // accept packet + PacketPassInterface_Done(&o->recv_if); + + // pass packet to device + if (local) { + o->device->output_func(o->device->output_func_user, data, data_len); + } + + // relay frame + if (relay_dest_peer) { + DPRelayRouter_SubmitFrame(&device->relay_router, &src_peer->relay_source, &relay_dest_peer->relay_sink, data, data_len, device->relay_flow_buffer_size, device->relay_flow_inactivity_time); + } +} + +int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time) +{ + ASSERT(device_mtu >= 0) + ASSERT(device_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + ASSERT(output_func) + ASSERT(relay_flow_buffer_size > 0) + + // init arguments + o->device_mtu = device_mtu; + o->output_func = output_func; + o->output_func_user = output_func_user; + o->reactor = reactor; + o->relay_flow_buffer_size = relay_flow_buffer_size; + o->relay_flow_inactivity_time = relay_flow_inactivity_time; + + // remember packet MTU + o->packet_mtu = DATAPROTO_MAX_OVERHEAD + o->device_mtu; + + // init relay router + if (!DPRelayRouter_Init(&o->relay_router, o->device_mtu, o->reactor)) { + BLog(BLOG_ERROR, "DPRelayRouter_Init failed"); + goto fail0; + } + + // have no peer ID + o->have_peer_id = 0; + + // init peers list + LinkedList1_Init(&o->peers_list); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void DPReceiveDevice_Free (DPReceiveDevice *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(LinkedList1_IsEmpty(&o->peers_list)) + + // free relay router + DPRelayRouter_Free(&o->relay_router); +} + +void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id) +{ + DebugObject_Access(&o->d_obj); + + // remember peer ID + o->peer_id = peer_id; + o->have_peer_id = 1; +} + +void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client) +{ + DebugObject_Access(&device->d_obj); + ASSERT(is_relay_client == 0 || is_relay_client == 1) + + // init arguments + o->device = device; + o->peer_id = peer_id; + o->decider_peer = decider_peer; + o->is_relay_client = is_relay_client; + + // init relay source + DPRelaySource_Init(&o->relay_source, &device->relay_router, o->peer_id, device->reactor); + + // init relay sink + DPRelaySink_Init(&o->relay_sink, o->peer_id); + + // have no sink + o->dp_sink = NULL; + + // insert to peers list + LinkedList1_Append(&device->peers_list, &o->list_node); + + DebugCounter_Init(&o->d_receivers_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPReceivePeer_Free (DPReceivePeer *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_receivers_ctr); + ASSERT(!o->dp_sink) + + // remove from peers list + LinkedList1_Remove(&o->device->peers_list, &o->list_node); + + // free relay sink + DPRelaySink_Free(&o->relay_sink); + + // free relay source + DPRelaySource_Free(&o->relay_source); +} + +void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->dp_sink) + ASSERT(dp_sink) + + // attach relay sink + DPRelaySink_Attach(&o->relay_sink, dp_sink); + + o->dp_sink = dp_sink; +} + +void DPReceivePeer_DetachSink (DPReceivePeer *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->dp_sink) + + // detach relay sink + DPRelaySink_Detach(&o->relay_sink); + + o->dp_sink = NULL; +} + +void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer) +{ + DebugObject_Access(&peer->d_obj); + DPReceiveDevice *device = peer->device; + + // init arguments + o->peer = peer; + + // remember device + o->device = device; + + // init receive interface + PacketPassInterface_Init(&o->recv_if, device->packet_mtu, (PacketPassInterface_handler_send)receiver_recv_handler_send, o, BReactor_PendingGroup(device->reactor)); + + DebugCounter_Increment(&peer->d_receivers_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPReceiveReceiver_Free (DPReceiveReceiver *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->peer->d_receivers_ctr); + + // free receive interface + PacketPassInterface_Free(&o->recv_if); +} + +PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->recv_if; +} diff --git a/external/badvpn_dns/client/DPReceive.h b/external/badvpn_dns/client/DPReceive.h new file mode 100644 index 00000000..ecc5a05a --- /dev/null +++ b/external/badvpn_dns/client/DPReceive.h @@ -0,0 +1,98 @@ +/** + * @file DPReceive.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Receive processing for the VPN client. + */ + +#ifndef BADVPN_CLIENT_DPRECEIVE_H +#define BADVPN_CLIENT_DPRECEIVE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*DPReceiveDevice_output_func) (void *output_user, uint8_t *data, int data_len); + +struct DPReceiveReceiver_s; + +typedef struct { + int device_mtu; + DPReceiveDevice_output_func output_func; + void *output_func_user; + BReactor *reactor; + int relay_flow_buffer_size; + int relay_flow_inactivity_time; + int packet_mtu; + DPRelayRouter relay_router; + int have_peer_id; + peerid_t peer_id; + LinkedList1 peers_list; + DebugObject d_obj; +} DPReceiveDevice; + +typedef struct { + DPReceiveDevice *device; + peerid_t peer_id; + FrameDeciderPeer *decider_peer; + int is_relay_client; + DPRelaySource relay_source; + DPRelaySink relay_sink; + DataProtoSink *dp_sink; + LinkedList1Node list_node; + DebugObject d_obj; + DebugCounter d_receivers_ctr; +} DPReceivePeer; + +typedef struct DPReceiveReceiver_s { + DPReceivePeer *peer; + DPReceiveDevice *device; + PacketPassInterface recv_if; + DebugObject d_obj; +} DPReceiveReceiver; + +int DPReceiveDevice_Init (DPReceiveDevice *o, int device_mtu, DPReceiveDevice_output_func output_func, void *output_func_user, BReactor *reactor, int relay_flow_buffer_size, int relay_flow_inactivity_time) WARN_UNUSED; +void DPReceiveDevice_Free (DPReceiveDevice *o); +void DPReceiveDevice_SetPeerID (DPReceiveDevice *o, peerid_t peer_id); + +void DPReceivePeer_Init (DPReceivePeer *o, DPReceiveDevice *device, peerid_t peer_id, FrameDeciderPeer *decider_peer, int is_relay_client); +void DPReceivePeer_Free (DPReceivePeer *o); +void DPReceivePeer_AttachSink (DPReceivePeer *o, DataProtoSink *dp_sink); +void DPReceivePeer_DetachSink (DPReceivePeer *o); + +void DPReceiveReceiver_Init (DPReceiveReceiver *o, DPReceivePeer *peer); +void DPReceiveReceiver_Free (DPReceiveReceiver *o); +PacketPassInterface * DPReceiveReceiver_GetInput (DPReceiveReceiver *o); + +#endif diff --git a/external/badvpn_dns/client/DPRelay.c b/external/badvpn_dns/client/DPRelay.c new file mode 100644 index 00000000..983e3ad3 --- /dev/null +++ b/external/badvpn_dns/client/DPRelay.c @@ -0,0 +1,307 @@ +/** + * @file DPRelay.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +#include + +static void flow_inactivity_handler (struct DPRelay_flow *flow); + +static struct DPRelay_flow * create_flow (DPRelaySource *src, DPRelaySink *sink, int num_packets, int inactivity_time) +{ + ASSERT(num_packets > 0) + + // allocate structure + struct DPRelay_flow *flow = (struct DPRelay_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "relay flow %d->%d: malloc failed", (int)src->source_id, (int)sink->dest_id); + goto fail0; + } + + // set src and sink + flow->src = src; + flow->sink = sink; + + // init DataProtoFlow + if (!DataProtoFlow_Init(&flow->dp_flow, &src->router->dp_source, src->source_id, sink->dest_id, num_packets, inactivity_time, flow, (DataProtoFlow_handler_inactivity)flow_inactivity_handler)) { + BLog(BLOG_ERROR, "relay flow %d->%d: DataProtoFlow_Init failed", (int)src->source_id, (int)sink->dest_id); + goto fail1; + } + + // insert to source list + LinkedList1_Append(&src->flows_list, &flow->src_list_node); + + // insert to sink list + LinkedList1_Append(&sink->flows_list, &flow->sink_list_node); + + // attach flow if needed + if (sink->dp_sink) { + DataProtoFlow_Attach(&flow->dp_flow, sink->dp_sink); + } + + BLog(BLOG_INFO, "relay flow %d->%d: created", (int)src->source_id, (int)sink->dest_id); + + return flow; + +fail1: + free(flow); +fail0: + return NULL; +} + +static void free_flow (struct DPRelay_flow *flow) +{ + // detach flow if needed + if (flow->sink->dp_sink) { + DataProtoFlow_Detach(&flow->dp_flow); + } + + // remove posible router reference + if (flow->src->router->current_flow == flow) { + flow->src->router->current_flow = NULL; + } + + // remove from sink list + LinkedList1_Remove(&flow->sink->flows_list, &flow->sink_list_node); + + // remove from source list + LinkedList1_Remove(&flow->src->flows_list, &flow->src_list_node); + + // free DataProtoFlow + DataProtoFlow_Free(&flow->dp_flow); + + // free structore + free(flow); +} + +static void flow_inactivity_handler (struct DPRelay_flow *flow) +{ + BLog(BLOG_INFO, "relay flow %d->%d: timed out", (int)flow->src->source_id, (int)flow->sink->dest_id); + + free_flow(flow); +} + +static struct DPRelay_flow * source_find_flow (DPRelaySource *o, DPRelaySink *sink) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node); + ASSERT(flow->src == o) + if (flow->sink == sink) { + return flow; + } + } + + return NULL; +} + +static void router_dp_source_handler (DPRelayRouter *o, const uint8_t *frame, int frame_len) +{ + DebugObject_Access(&o->d_obj); + + if (!o->current_flow) { + return; + } + + // route frame to current flow + DataProtoFlow_Route(&o->current_flow->dp_flow, 0); + + // set no current flow + o->current_flow = NULL; +} + +int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor) +{ + ASSERT(frame_mtu >= 0) + ASSERT(frame_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + + // init arguments + o->frame_mtu = frame_mtu; + + // init BufferWriter + BufferWriter_Init(&o->writer, frame_mtu, BReactor_PendingGroup(reactor)); + + // init DataProtoSource + if (!DataProtoSource_Init(&o->dp_source, BufferWriter_GetOutput(&o->writer), (DataProtoSource_handler)router_dp_source_handler, o, reactor)) { + BLog(BLOG_ERROR, "DataProtoSource_Init failed"); + goto fail1; + } + + // have no current flow + o->current_flow = NULL; + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + BufferWriter_Free(&o->writer); + return 0; +} + +void DPRelayRouter_Free (DPRelayRouter *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + ASSERT(!o->current_flow) // have no sources + + // free DataProtoSource + DataProtoSource_Free(&o->dp_source); + + // free BufferWriter + BufferWriter_Free(&o->writer); +} + +void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time) +{ + DebugObject_Access(&o->d_obj); + DebugObject_Access(&src->d_obj); + DebugObject_Access(&sink->d_obj); + ASSERT(!o->current_flow) + ASSERT(src->router == o) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->frame_mtu) + ASSERT(num_packets > 0) + + // get memory location + uint8_t *out; + if (!BufferWriter_StartPacket(&o->writer, &out)) { + BLog(BLOG_ERROR, "BufferWriter_StartPacket failed for frame %d->%d !?", (int)src->source_id, (int)sink->dest_id); + return; + } + + // write frame + memcpy(out, data, data_len); + + // submit frame + BufferWriter_EndPacket(&o->writer, data_len); + + // get a flow + // this comes _after_ writing the packet, in case flow initialization schedules jobs + struct DPRelay_flow *flow = source_find_flow(src, sink); + if (!flow) { + if (!(flow = create_flow(src, sink, num_packets, inactivity_time))) { + return; + } + } + + // remember flow so we know where to route the frame in router_dp_source_handler + o->current_flow = flow; +} + +void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor) +{ + DebugObject_Access(&router->d_obj); + + // init arguments + o->router = router; + o->source_id = source_id; + + // init flows list + LinkedList1_Init(&o->flows_list); + + DebugCounter_Increment(&o->router->d_ctr); + DebugObject_Init(&o->d_obj); +} + +void DPRelaySource_Free (DPRelaySource *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->router->d_ctr); + + // free flows, detaching them if needed + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&o->flows_list)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node); + free_flow(flow); + } +} + +void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id) +{ + // init arguments + o->dest_id = dest_id; + + // init flows list + LinkedList1_Init(&o->flows_list); + + // have no sink + o->dp_sink = NULL; + + DebugObject_Init(&o->d_obj); +} + +void DPRelaySink_Free (DPRelaySink *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(!o->dp_sink) + + // free flows + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&o->flows_list)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + free_flow(flow); + } +} + +void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->dp_sink) + ASSERT(dp_sink) + + // attach flows + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + DataProtoFlow_Attach(&flow->dp_flow, dp_sink); + } + + // set sink + o->dp_sink = dp_sink; +} + +void DPRelaySink_Detach (DPRelaySink *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->dp_sink) + + // detach flows + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list); node; node = LinkedList1Node_Next(node)) { + struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node); + DataProtoFlow_Detach(&flow->dp_flow); + } + + // set no sink + o->dp_sink = NULL; +} diff --git a/external/badvpn_dns/client/DPRelay.h b/external/badvpn_dns/client/DPRelay.h new file mode 100644 index 00000000..c5e16eb4 --- /dev/null +++ b/external/badvpn_dns/client/DPRelay.h @@ -0,0 +1,89 @@ +/** + * @file DPRelay.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CLIENT_DPRELAY_H +#define BADVPN_CLIENT_DPRELAY_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct DPRelay_flow; + +typedef struct { + int frame_mtu; + BufferWriter writer; + DataProtoSource dp_source; + struct DPRelay_flow *current_flow; + DebugObject d_obj; + DebugCounter d_ctr; +} DPRelayRouter; + +typedef struct { + DPRelayRouter *router; + peerid_t source_id; + LinkedList1 flows_list; + DebugObject d_obj; +} DPRelaySource; + +typedef struct { + peerid_t dest_id; + LinkedList1 flows_list; + DataProtoSink *dp_sink; + DebugObject d_obj; +} DPRelaySink; + +struct DPRelay_flow { + DPRelaySource *src; + DPRelaySink *sink; + DataProtoFlow dp_flow; + LinkedList1Node src_list_node; + LinkedList1Node sink_list_node; +}; + +int DPRelayRouter_Init (DPRelayRouter *o, int frame_mtu, BReactor *reactor) WARN_UNUSED; +void DPRelayRouter_Free (DPRelayRouter *o); +void DPRelayRouter_SubmitFrame (DPRelayRouter *o, DPRelaySource *src, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets, int inactivity_time); + +void DPRelaySource_Init (DPRelaySource *o, DPRelayRouter *router, peerid_t source_id, BReactor *reactor); +void DPRelaySource_Free (DPRelaySource *o); + +void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id); +void DPRelaySink_Free (DPRelaySink *o); +void DPRelaySink_Attach (DPRelaySink *o, DataProtoSink *dp_sink); +void DPRelaySink_Detach (DPRelaySink *o); + +#endif diff --git a/external/badvpn_dns/client/DataProto.c b/external/badvpn_dns/client/DataProto.c new file mode 100644 index 00000000..255045a5 --- /dev/null +++ b/external/badvpn_dns/client/DataProto.c @@ -0,0 +1,566 @@ +/** + * @file DataProto.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +static void monitor_handler (DataProtoSink *o); +static void refresh_up_job (DataProtoSink *o); +static void receive_timer_handler (DataProtoSink *o); +static void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len); +static void up_job_handler (DataProtoSink *o); +static void flow_buffer_free (struct DataProtoFlow_buffer *b); +static void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink); +static void flow_buffer_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b); +static void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b); + +void monitor_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + + // send keep-alive + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); +} + +void refresh_up_job (DataProtoSink *o) +{ + if (o->up != o->up_report) { + BPending_Set(&o->up_job); + } else { + BPending_Unset(&o->up_job); + } +} + +void receive_timer_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + + // consider down + o->up = 0; + + refresh_up_job(o); +} + +void notifier_handler (DataProtoSink *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= sizeof(struct dataproto_header)) + + int flags = 0; + + // if we are receiving keepalives, set the flag + if (BTimer_IsRunning(&o->receive_timer)) { + flags |= DATAPROTO_FLAGS_RECEIVING_KEEPALIVES; + } + + // modify existing packet here + struct dataproto_header header; + memcpy(&header, data, sizeof(header)); + header.flags = hton8(flags); + memcpy(data, &header, sizeof(header)); +} + +void up_job_handler (DataProtoSink *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up != o->up_report) + + o->up_report = o->up; + + o->handler(o->user, o->up); + return; +} + +void source_router_handler (DataProtoSource *o, uint8_t *buf, int recv_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(buf) + ASSERT(recv_len >= 0) + ASSERT(recv_len <= o->frame_mtu) + + // remember packet + o->current_buf = buf; + o->current_recv_len = recv_len; + + // call handler + o->handler(o->user, buf + DATAPROTO_MAX_OVERHEAD, recv_len); + return; +} + +void flow_buffer_free (struct DataProtoFlow_buffer *b) +{ + ASSERT(!b->sink) + + // free route buffer + RouteBuffer_Free(&b->rbuf); + + // free inactivity monitor + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Free(&b->monitor); + } + + // free connector + PacketPassConnector_Free(&b->connector); + + // free buffer structure + free(b); +} + +void flow_buffer_attach (struct DataProtoFlow_buffer *b, DataProtoSink *sink) +{ + ASSERT(!b->sink) + + // init queue flow + PacketPassFairQueueFlow_Init(&b->sink_qflow, &sink->queue); + + // connect to queue flow + PacketPassConnector_ConnectOutput(&b->connector, PacketPassFairQueueFlow_GetInput(&b->sink_qflow)); + + // set DataProto + b->sink = sink; +} + +void flow_buffer_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + // disconnect from queue flow + PacketPassConnector_DisconnectOutput(&b->connector); + + // free queue flow + PacketPassFairQueueFlow_Free(&b->sink_qflow); + + // clear reference to this buffer in the sink + if (b->sink->detaching_buffer == b) { + b->sink->detaching_buffer = NULL; + } + + // set no DataProto + b->sink = NULL; +} + +void flow_buffer_schedule_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) + ASSERT(!b->sink->detaching_buffer || b->sink->detaching_buffer == b) + + if (b->sink->detaching_buffer == b) { + return; + } + + // request cancel + PacketPassFairQueueFlow_RequestCancel(&b->sink_qflow); + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&b->sink_qflow, (PacketPassFairQueue_handler_busy)flow_buffer_qflow_handler_busy, b); + + // remember this buffer in the sink so it can handle us if it goes away + b->sink->detaching_buffer = b; +} + +void flow_buffer_finish_detach (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(b->sink->detaching_buffer == b) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + // detach + flow_buffer_detach(b); + + if (!b->flow) { + // free + flow_buffer_free(b); + } else if (b->flow->sink_desired) { + // attach + flow_buffer_attach(b, b->flow->sink_desired); + } +} + +void flow_buffer_qflow_handler_busy (struct DataProtoFlow_buffer *b) +{ + ASSERT(b->sink) + ASSERT(b->sink->detaching_buffer == b) + PacketPassFairQueueFlow_AssertFree(&b->sink_qflow); + + flow_buffer_finish_detach(b); +} + +int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user) +{ + ASSERT(PacketPassInterface_HasCancel(output)) + ASSERT(PacketPassInterface_GetMTU(output) >= DATAPROTO_MAX_OVERHEAD) + + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // set frame MTU + o->frame_mtu = PacketPassInterface_GetMTU(output) - DATAPROTO_MAX_OVERHEAD; + + // init notifier + PacketPassNotifier_Init(&o->notifier, output, BReactor_PendingGroup(o->reactor)); + PacketPassNotifier_SetHandler(&o->notifier, (PacketPassNotifier_handler_notify)notifier_handler, o); + + // init monitor + PacketPassInactivityMonitor_Init(&o->monitor, PacketPassNotifier_GetInput(&o->notifier), o->reactor, keepalive_time, (PacketPassInactivityMonitor_handler)monitor_handler, o); + PacketPassInactivityMonitor_Force(&o->monitor); + + // init queue + if (!PacketPassFairQueue_Init(&o->queue, PacketPassInactivityMonitor_GetInput(&o->monitor), BReactor_PendingGroup(o->reactor), 1, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail1; + } + + // init keepalive queue flow + PacketPassFairQueueFlow_Init(&o->ka_qflow, &o->queue); + + // init keepalive source + DataProtoKeepaliveSource_Init(&o->ka_source, BReactor_PendingGroup(o->reactor)); + + // init keepalive blocker + PacketRecvBlocker_Init(&o->ka_blocker, DataProtoKeepaliveSource_GetOutput(&o->ka_source), BReactor_PendingGroup(o->reactor)); + + // init keepalive buffer + if (!SinglePacketBuffer_Init(&o->ka_buffer, PacketRecvBlocker_GetOutput(&o->ka_blocker), PacketPassFairQueueFlow_GetInput(&o->ka_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init receive timer + BTimer_Init(&o->receive_timer, tolerance_time, (BTimer_handler)receive_timer_handler, o); + + // init handler job + BPending_Init(&o->up_job, BReactor_PendingGroup(o->reactor), (BPending_handler)up_job_handler, o); + + // set not up + o->up = 0; + o->up_report = 0; + + // set no detaching buffer + o->detaching_buffer = NULL; + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + PacketRecvBlocker_Free(&o->ka_blocker); + DataProtoKeepaliveSource_Free(&o->ka_source); + PacketPassFairQueueFlow_Free(&o->ka_qflow); + PacketPassFairQueue_Free(&o->queue); +fail1: + PacketPassInactivityMonitor_Free(&o->monitor); + PacketPassNotifier_Free(&o->notifier); + return 0; +} + +void DataProtoSink_Free (DataProtoSink *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + // allow freeing queue flows + PacketPassFairQueue_PrepareFree(&o->queue); + + // release detaching buffer + if (o->detaching_buffer) { + ASSERT(!o->detaching_buffer->flow || o->detaching_buffer->flow->sink_desired != o) + flow_buffer_finish_detach(o->detaching_buffer); + } + + // free handler job + BPending_Free(&o->up_job); + + // free receive timer + BReactor_RemoveTimer(o->reactor, &o->receive_timer); + + // free keepalive buffer + SinglePacketBuffer_Free(&o->ka_buffer); + + // free keepalive blocker + PacketRecvBlocker_Free(&o->ka_blocker); + + // free keepalive source + DataProtoKeepaliveSource_Free(&o->ka_source); + + // free keepalive queue flow + PacketPassFairQueueFlow_Free(&o->ka_qflow); + + // free queue + PacketPassFairQueue_Free(&o->queue); + + // free monitor + PacketPassInactivityMonitor_Free(&o->monitor); + + // free notifier + PacketPassNotifier_Free(&o->notifier); +} + +void DataProtoSink_Received (DataProtoSink *o, int peer_receiving) +{ + ASSERT(peer_receiving == 0 || peer_receiving == 1) + DebugObject_Access(&o->d_obj); + + // reset receive timer + BReactor_SetTimer(o->reactor, &o->receive_timer); + + if (!peer_receiving) { + // peer reports not receiving, consider down + o->up = 0; + // send keep-alive to converge faster + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); + } else { + // consider up + o->up = 1; + } + + refresh_up_job(o); +} + +int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - DATAPROTO_MAX_OVERHEAD) + ASSERT(handler) + + // init arguments + o->handler = handler; + o->user = user; + o->reactor = reactor; + + // remember frame MTU + o->frame_mtu = PacketRecvInterface_GetMTU(input); + + // init router + if (!PacketRouter_Init(&o->router, DATAPROTO_MAX_OVERHEAD + o->frame_mtu, DATAPROTO_MAX_OVERHEAD, input, (PacketRouter_handler)source_router_handler, o, BReactor_PendingGroup(reactor))) { + BLog(BLOG_ERROR, "PacketRouter_Init failed"); + goto fail0; + } + + DebugCounter_Init(&o->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void DataProtoSource_Free (DataProtoSource *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + // free router + PacketRouter_Free(&o->router); +} + +int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user, + DataProtoFlow_handler_inactivity handler_inactivity) +{ + DebugObject_Access(&source->d_obj); + ASSERT(num_packets > 0) + ASSERT(!(inactivity_time >= 0) || handler_inactivity) + + // init arguments + o->source = source; + o->source_id = source_id; + o->dest_id = dest_id; + + // set no desired sink + o->sink_desired = NULL; + + // allocate buffer structure + struct DataProtoFlow_buffer *b = (struct DataProtoFlow_buffer *)malloc(sizeof(*b)); + if (!b) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + o->b = b; + + // set parent + b->flow = o; + + // remember inactivity time + b->inactivity_time = inactivity_time; + + // init connector + PacketPassConnector_Init(&b->connector, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, BReactor_PendingGroup(source->reactor)); + + // init inactivity monitor + PacketPassInterface *buf_out = PacketPassConnector_GetInput(&b->connector); + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Init(&b->monitor, buf_out, source->reactor, b->inactivity_time, handler_inactivity, user); + buf_out = PacketPassInactivityMonitor_GetInput(&b->monitor); + } + + // init route buffer + if (!RouteBuffer_Init(&b->rbuf, DATAPROTO_MAX_OVERHEAD + source->frame_mtu, buf_out, num_packets)) { + BLog(BLOG_ERROR, "RouteBuffer_Init failed"); + goto fail1; + } + + // set no sink + b->sink = NULL; + + DebugCounter_Increment(&source->d_ctr); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_Free(&b->monitor); + } + PacketPassConnector_Free(&b->connector); + free(b); +fail0: + return 0; +} + +void DataProtoFlow_Free (DataProtoFlow *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->source->d_ctr); + ASSERT(!o->sink_desired) + struct DataProtoFlow_buffer *b = o->b; + + if (b->sink) { + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach, free buffer after detach + flow_buffer_schedule_detach(b); + b->flow = NULL; + + // remove inactivity handler + if (b->inactivity_time >= 0) { + PacketPassInactivityMonitor_SetHandler(&b->monitor, NULL, NULL); + } + } else { + // detach and free buffer now + flow_buffer_detach(b); + flow_buffer_free(b); + } + } else { + // free buffer + flow_buffer_free(b); + } +} + +void DataProtoFlow_Route (DataProtoFlow *o, int more) +{ + DebugObject_Access(&o->d_obj); + PacketRouter_AssertRoute(&o->source->router); + ASSERT(o->source->current_buf) + ASSERT(more == 0 || more == 1) + struct DataProtoFlow_buffer *b = o->b; + + // write header. Don't set flags, it will be set in notifier_handler. + struct dataproto_header header; + struct dataproto_peer_id id; + header.from_id = htol16(o->source_id); + header.num_peer_ids = htol16(1); + id.id = htol16(o->dest_id); + memcpy(o->source->current_buf, &header, sizeof(header)); + memcpy(o->source->current_buf + sizeof(header), &id, sizeof(id)); + + // route + uint8_t *next_buf; + if (!PacketRouter_Route(&o->source->router, DATAPROTO_MAX_OVERHEAD + o->source->current_recv_len, &b->rbuf, + &next_buf, DATAPROTO_MAX_OVERHEAD, (more ? o->source->current_recv_len : 0) + )) { + BLog(BLOG_NOTICE, "buffer full: %d->%d", (int)o->source_id, (int)o->dest_id); + return; + } + + // remember next buffer, or don't allow further routing if more==0 + o->source->current_buf = (more ? next_buf : NULL); +} + +void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink) +{ + DebugObject_Access(&o->d_obj); + DebugObject_Access(&sink->d_obj); + ASSERT(!o->sink_desired) + ASSERT(sink) + ASSERT(o->source->frame_mtu <= sink->frame_mtu) + struct DataProtoFlow_buffer *b = o->b; + + if (b->sink) { + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach and reattach + flow_buffer_schedule_detach(b); + } else { + // detach and reattach now + flow_buffer_detach(b); + flow_buffer_attach(b, sink); + } + } else { + // attach + flow_buffer_attach(b, sink); + } + + // set desired sink + o->sink_desired = sink; + + DebugCounter_Increment(&sink->d_ctr); +} + +void DataProtoFlow_Detach (DataProtoFlow *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sink_desired) + struct DataProtoFlow_buffer *b = o->b; + ASSERT(b->sink) + + DataProtoSink *sink = o->sink_desired; + + if (PacketPassFairQueueFlow_IsBusy(&b->sink_qflow)) { + // schedule detach + flow_buffer_schedule_detach(b); + } else { + // detach now + flow_buffer_detach(b); + } + + // set no desired sink + o->sink_desired = NULL; + + DebugCounter_Decrement(&sink->d_ctr); +} diff --git a/external/badvpn_dns/client/DataProto.h b/external/badvpn_dns/client/DataProto.h new file mode 100644 index 00000000..0da3a20a --- /dev/null +++ b/external/badvpn_dns/client/DataProto.h @@ -0,0 +1,237 @@ +/** + * @file DataProto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mudule for frame sending used in the VPN client program. + */ + +#ifndef BADVPN_CLIENT_DATAPROTO_H +#define BADVPN_CLIENT_DATAPROTO_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*DataProtoSink_handler) (void *user, int up); +typedef void (*DataProtoSource_handler) (void *user, const uint8_t *frame, int frame_len); +typedef void (*DataProtoFlow_handler_inactivity) (void *user); + +struct DataProtoFlow_buffer; + +/** + * Frame destination. + * Represents a peer as a destination for sending frames to. + */ +typedef struct { + BReactor *reactor; + int frame_mtu; + PacketPassFairQueue queue; + PacketPassInactivityMonitor monitor; + PacketPassNotifier notifier; + DataProtoKeepaliveSource ka_source; + PacketRecvBlocker ka_blocker; + SinglePacketBuffer ka_buffer; + PacketPassFairQueueFlow ka_qflow; + BTimer receive_timer; + int up; + int up_report; + DataProtoSink_handler handler; + void *user; + BPending up_job; + struct DataProtoFlow_buffer *detaching_buffer; + DebugObject d_obj; + DebugCounter d_ctr; +} DataProtoSink; + +/** + * Receives frames from a {@link PacketRecvInterface} input and + * allows the user to route them to buffers in {@link DataProtoFlow}'s. + */ +typedef struct { + DataProtoSource_handler handler; + void *user; + BReactor *reactor; + int frame_mtu; + PacketRouter router; + uint8_t *current_buf; + int current_recv_len; + DebugObject d_obj; + DebugCounter d_ctr; +} DataProtoSource; + +/** + * Contains a buffer for frames from a specific peer to a specific peer. + * Receives frames from a {@link DataProtoSource} as routed by the user. + * Can be attached to a {@link DataProtoSink} to send out frames. + */ +typedef struct { + DataProtoSource *source; + peerid_t source_id; + peerid_t dest_id; + DataProtoSink *sink_desired; + struct DataProtoFlow_buffer *b; + DebugObject d_obj; +} DataProtoFlow; + +struct DataProtoFlow_buffer { + DataProtoFlow *flow; + int inactivity_time; + RouteBuffer rbuf; + PacketPassInactivityMonitor monitor; + PacketPassConnector connector; + DataProtoSink *sink; + PacketPassFairQueueFlow sink_qflow; +}; + +/** + * Initializes the sink. + * + * @param o the object + * @param reactor reactor we live in + * @param output output interface. Must support cancel functionality. Its MTU must be + * >=DATAPROTO_MAX_OVERHEAD. + * @param keepalive_time keepalive time + * @param tolerance_time after how long of not having received anything from the peer + * to consider the link down + * @param handler up state handler + * @param user value to pass to handler + * @return 1 on success, 0 on failure + */ +int DataProtoSink_Init (DataProtoSink *o, BReactor *reactor, PacketPassInterface *output, btime_t keepalive_time, btime_t tolerance_time, DataProtoSink_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the sink. + * There must be no local sources attached. + * + * @param o the object + */ +void DataProtoSink_Free (DataProtoSink *o); + +/** + * Notifies the sink that a packet was received from the peer. + * Must not be in freeing state. + * + * @param o the object + * @param peer_receiving whether the DATAPROTO_FLAGS_RECEIVING_KEEPALIVES flag was set in the packet. + * Must be 0 or 1. + */ +void DataProtoSink_Received (DataProtoSink *o, int peer_receiving); + +/** + * Initiazes the source. + * + * @param o the object + * @param input frame input. Its input MTU must be <= INT_MAX - DATAPROTO_MAX_OVERHEAD. + * @param handler handler called when a frame arrives to allow the user to route it to + * appropriate {@link DataProtoFlow}'s. + * @param user value passed to handler + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int DataProtoSource_Init (DataProtoSource *o, PacketRecvInterface *input, DataProtoSource_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the source. + * There must be no {@link DataProtoFlow}'s using this source. + * + * @param o the object + */ +void DataProtoSource_Free (DataProtoSource *o); + +/** + * Initializes the flow. + * The flow is initialized in not attached state. + * + * @param o the object + * @param source source to receive frames from + * @param source_id source peer ID to encode in the headers (i.e. our ID) + * @param dest_id destination peer ID to encode in the headers (i.e. ID if the peer this + * flow belongs to) + * @param num_packets number of packets the buffer should hold. Must be >0. + * @param inactivity_time milliseconds of output inactivity after which to call the + * inactivity handler; <0 to disable. Note that the flow is considered + * active as long as its buffer is non-empty, even if is not attached to + * a {@link DataProtoSink}. + * @param user value to pass to handler + * @param handler_inactivity inactivity handler, if inactivity_time >=0 + * @return 1 on success, 0 on failure + */ +int DataProtoFlow_Init (DataProtoFlow *o, DataProtoSource *source, peerid_t source_id, peerid_t dest_id, int num_packets, int inactivity_time, void *user, + DataProtoFlow_handler_inactivity handler_inactivity) WARN_UNUSED; + +/** + * Frees the flow. + * The flow must be in not attached state. + * + * @param o the object + */ +void DataProtoFlow_Free (DataProtoFlow *o); + +/** + * Routes a frame from the flow's source to this flow. + * Must be called from within the job context of the {@link DataProtoSource_handler} handler. + * Must not be called after this has been called with more=0 for the current frame. + * + * @param o the object + * @param more whether the current frame may have to be routed to more + * flows. If 0, must not be called again until the handler is + * called for the next frame. Must be 0 or 1. + */ +void DataProtoFlow_Route (DataProtoFlow *o, int more); + +/** + * Attaches the flow to a sink. + * The flow must be in not attached state. + * + * @param o the object + * @param sink sink to attach to. This flow's frame_mtu must be <= + * (output MTU of sink) - DATAPROTO_MAX_OVERHEAD. + */ +void DataProtoFlow_Attach (DataProtoFlow *o, DataProtoSink *sink); + +/** + * Detaches the flow from a destination. + * The flow must be in attached state. + * + * @param o the object + */ +void DataProtoFlow_Detach (DataProtoFlow *o); + +#endif diff --git a/external/badvpn_dns/client/DataProtoKeepaliveSource.c b/external/badvpn_dns/client/DataProtoKeepaliveSource.c new file mode 100644 index 00000000..834c42f7 --- /dev/null +++ b/external/badvpn_dns/client/DataProtoKeepaliveSource.c @@ -0,0 +1,72 @@ +/** + * @file DataProtoKeepaliveSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "DataProtoKeepaliveSource.h" + +static void output_handler_recv (DataProtoKeepaliveSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + struct dataproto_header header; + header.flags = htol8(0); + header.from_id = htol16(0); + header.num_peer_ids = htol16(0); + memcpy(data, &header, sizeof(header)); + + // finish packet + PacketRecvInterface_Done(&o->output, sizeof(struct dataproto_header)); +} + +void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg) +{ + // init output + PacketRecvInterface_Init(&o->output, sizeof(struct dataproto_header), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/client/DataProtoKeepaliveSource.h b/external/badvpn_dns/client/DataProtoKeepaliveSource.h new file mode 100644 index 00000000..4488e249 --- /dev/null +++ b/external/badvpn_dns/client/DataProtoKeepaliveSource.h @@ -0,0 +1,73 @@ +/** + * @file DataProtoKeepaliveSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} source which provides DataProto keepalive packets. + */ + +#ifndef BADVPN_DATAPROTOKEEPALIVESOURCE_H +#define BADVPN_DATAPROTOKEEPALIVESOURCE_H + +#include +#include + +/** + * A {@link PacketRecvInterface} source which provides DataProto keepalive packets. + * These packets have no payload, no destination peers and flags zero. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface output; +} DataProtoKeepaliveSource; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void DataProtoKeepaliveSource_Init (DataProtoKeepaliveSource *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void DataProtoKeepaliveSource_Free (DataProtoKeepaliveSource *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be sizeof(struct dataproto_header). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * DataProtoKeepaliveSource_GetOutput (DataProtoKeepaliveSource *o); + +#endif diff --git a/external/badvpn_dns/client/DatagramPeerIO.c b/external/badvpn_dns/client/DatagramPeerIO.c new file mode 100644 index 00000000..e3a8f686 --- /dev/null +++ b/external/badvpn_dns/client/DatagramPeerIO.c @@ -0,0 +1,425 @@ +/** + * @file DatagramPeerIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#define DATAGRAMPEERIO_MODE_NONE 0 +#define DATAGRAMPEERIO_MODE_CONNECT 1 +#define DATAGRAMPEERIO_MODE_BIND 2 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void init_io (DatagramPeerIO *o); +static void free_io (DatagramPeerIO *o); +static void dgram_handler (DatagramPeerIO *o, int event); +static void reset_mode (DatagramPeerIO *o); +static void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len); + +void init_io (DatagramPeerIO *o) +{ + // init dgram recv interface + BDatagram_RecvAsync_Init(&o->dgram, o->effective_socket_mtu); + + // connect source + PacketRecvConnector_ConnectInput(&o->recv_connector, BDatagram_RecvAsync_GetIf(&o->dgram)); + + // init dgram send interface + BDatagram_SendAsync_Init(&o->dgram, o->effective_socket_mtu); + + // connect sink + PacketPassConnector_ConnectOutput(&o->send_connector, BDatagram_SendAsync_GetIf(&o->dgram)); +} + +void free_io (DatagramPeerIO *o) +{ + // disconnect sink + PacketPassConnector_DisconnectOutput(&o->send_connector); + + // free dgram send interface + BDatagram_SendAsync_Free(&o->dgram); + + // disconnect source + PacketRecvConnector_DisconnectInput(&o->recv_connector); + + // free dgram recv interface + BDatagram_RecvAsync_Free(&o->dgram); +} + +void dgram_handler (DatagramPeerIO *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) + + PeerLog(o, BLOG_NOTICE, "error"); + + // reset mode + reset_mode(o); + + // report error + if (o->handler_error) { + o->handler_error(o->user); + return; + } +} + +void reset_mode (DatagramPeerIO *o) +{ + ASSERT(o->mode == DATAGRAMPEERIO_MODE_NONE || o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) + + if (o->mode == DATAGRAMPEERIO_MODE_NONE) { + return; + } + + // remove recv notifier handler + PacketPassNotifier_SetHandler(&o->recv_notifier, NULL, NULL); + + // free I/O + free_io(o); + + // free datagram object + BDatagram_Free(&o->dgram); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_NONE; +} + +void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len) +{ + ASSERT(o->mode == DATAGRAMPEERIO_MODE_BIND) + DebugObject_Access(&o->d_obj); + + // obtain addresses from last received packet + BAddr addr; + BIPAddr local_addr; + ASSERT_EXECUTE(BDatagram_GetLastReceiveAddrs(&o->dgram, &addr, &local_addr)) + + // check address family just in case + if (!BDatagram_AddressFamilySupported(addr.type)) { + PeerLog(o, BLOG_ERROR, "unsupported receive address"); + return; + } + + // update addresses + BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); +} + +int DatagramPeerIO_Init ( + DatagramPeerIO *o, + BReactor *reactor, + int payload_mtu, + int socket_mtu, + struct spproto_security_params sp_params, + btime_t latency, + int num_frames, + PacketPassInterface *recv_userif, + int otp_warning_count, + BThreadWorkDispatcher *twd, + void *user, + BLog_logfunc logfunc, + DatagramPeerIO_handler_error handler_error, + DatagramPeerIO_handler_otp_warning handler_otp_warning, + DatagramPeerIO_handler_otp_ready handler_otp_ready +) +{ + ASSERT(payload_mtu >= 0) + ASSERT(socket_mtu >= 0) + spproto_assert_security_params(sp_params); + ASSERT(num_frames > 0) + ASSERT(PacketPassInterface_GetMTU(recv_userif) >= payload_mtu) + if (SPPROTO_HAVE_OTP(sp_params)) { + ASSERT(otp_warning_count > 0) + ASSERT(otp_warning_count <= sp_params.otp_num) + } + + // set parameters + o->reactor = reactor; + o->payload_mtu = payload_mtu; + o->sp_params = sp_params; + o->user = user; + o->logfunc = logfunc; + o->handler_error = handler_error; + + // check num frames (for FragmentProtoAssembler) + if (num_frames >= FPA_MAX_TIME) { + PeerLog(o, BLOG_ERROR, "num_frames is too big"); + goto fail0; + } + + // check payload MTU (for FragmentProto) + if (o->payload_mtu > UINT16_MAX) { + PeerLog(o, BLOG_ERROR, "payload MTU is too big"); + goto fail0; + } + + // calculate SPProto payload MTU + if ((o->spproto_payload_mtu = spproto_payload_mtu_for_carrier_mtu(o->sp_params, socket_mtu)) <= (int)sizeof(struct fragmentproto_chunk_header)) { + PeerLog(o, BLOG_ERROR, "socket MTU is too small"); + goto fail0; + } + + // calculate effective socket MTU + if ((o->effective_socket_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->spproto_payload_mtu)) < 0) { + PeerLog(o, BLOG_ERROR, "spproto_carrier_mtu_for_payload_mtu failed !?"); + goto fail0; + } + + // init receiving + + // init assembler + if (!FragmentProtoAssembler_Init(&o->recv_assembler, o->spproto_payload_mtu, recv_userif, num_frames, fragmentproto_max_chunks_for_frame(o->spproto_payload_mtu, o->payload_mtu), + BReactor_PendingGroup(o->reactor), o->user, o->logfunc + )) { + PeerLog(o, BLOG_ERROR, "FragmentProtoAssembler_Init failed"); + goto fail0; + } + + // init notifier + PacketPassNotifier_Init(&o->recv_notifier, FragmentProtoAssembler_GetInput(&o->recv_assembler), BReactor_PendingGroup(o->reactor)); + + // init decoder + if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor), twd, o->user, o->logfunc)) { + PeerLog(o, BLOG_ERROR, "SPProtoDecoder_Init failed"); + goto fail1; + } + SPProtoDecoder_SetHandlers(&o->recv_decoder, handler_otp_ready, user); + + // init connector + PacketRecvConnector_Init(&o->recv_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->recv_buffer, PacketRecvConnector_GetOutput(&o->recv_connector), SPProtoDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init sending base + + // init disassembler + FragmentProtoDisassembler_Init(&o->send_disassembler, o->reactor, o->payload_mtu, o->spproto_payload_mtu, -1, latency); + + // init encoder + if (!SPProtoEncoder_Init(&o->send_encoder, FragmentProtoDisassembler_GetOutput(&o->send_disassembler), o->sp_params, otp_warning_count, BReactor_PendingGroup(o->reactor), twd)) { + PeerLog(o, BLOG_ERROR, "SPProtoEncoder_Init failed"); + goto fail3; + } + SPProtoEncoder_SetHandlers(&o->send_encoder, handler_otp_warning, user); + + // init connector + PacketPassConnector_Init(&o->send_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->send_buffer, SPProtoEncoder_GetOutput(&o->send_encoder), PacketPassConnector_GetInput(&o->send_connector), BReactor_PendingGroup(o->reactor))) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + // set mode + o->mode = DATAGRAMPEERIO_MODE_NONE; + + DebugObject_Init(&o->d_obj); + return 1; + +fail4: + PacketPassConnector_Free(&o->send_connector); + SPProtoEncoder_Free(&o->send_encoder); +fail3: + FragmentProtoDisassembler_Free(&o->send_disassembler); + SinglePacketBuffer_Free(&o->recv_buffer); +fail2: + PacketRecvConnector_Free(&o->recv_connector); + SPProtoDecoder_Free(&o->recv_decoder); +fail1: + PacketPassNotifier_Free(&o->recv_notifier); + FragmentProtoAssembler_Free(&o->recv_assembler); +fail0: + return 0; +} + +void DatagramPeerIO_Free (DatagramPeerIO *o) +{ + DebugObject_Free(&o->d_obj); + + // reset mode + reset_mode(o); + + // free sending base + SinglePacketBuffer_Free(&o->send_buffer); + PacketPassConnector_Free(&o->send_connector); + SPProtoEncoder_Free(&o->send_encoder); + FragmentProtoDisassembler_Free(&o->send_disassembler); + + // free receiving + SinglePacketBuffer_Free(&o->recv_buffer); + PacketRecvConnector_Free(&o->recv_connector); + SPProtoDecoder_Free(&o->recv_decoder); + PacketPassNotifier_Free(&o->recv_notifier); + FragmentProtoAssembler_Free(&o->recv_assembler); +} + +PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o) +{ + DebugObject_Access(&o->d_obj); + + return FragmentProtoDisassembler_GetInput(&o->send_disassembler); +} + +int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + + // reset mode + reset_mode(o); + + // check address + if (!BDatagram_AddressFamilySupported(addr.type)) { + PeerLog(o, BLOG_ERROR, "BDatagram_AddressFamilySupported failed"); + goto fail0; + } + + // init dgram + if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { + PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // set send address + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); + + // init I/O + init_io(o); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_CONNECT; + + return 1; + +fail0: + return 0; +} + +int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // reset mode + reset_mode(o); + + // init dgram + if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { + PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // bind dgram + if (!BDatagram_Bind(&o->dgram, addr)) { + PeerLog(o, BLOG_INFO, "BDatagram_Bind failed"); + goto fail1; + } + + // init I/O + init_io(o); + + // set recv notifier handler + PacketPassNotifier_SetHandler(&o->recv_notifier, (PacketPassNotifier_handler_notify)recv_decoder_notifier_handler, o); + + // set mode + o->mode = DATAGRAMPEERIO_MODE_BIND; + + return 1; + +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // set sending key + SPProtoEncoder_SetEncryptionKey(&o->send_encoder, encryption_key); + + // set receiving key + SPProtoDecoder_SetEncryptionKey(&o->recv_decoder, encryption_key); +} + +void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove sending key + SPProtoEncoder_RemoveEncryptionKey(&o->send_encoder); + + // remove receiving key + SPProtoDecoder_RemoveEncryptionKey(&o->recv_decoder); +} + +void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // set sending seed + SPProtoEncoder_SetOTPSeed(&o->send_encoder, seed_id, key, iv); +} + +void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove sending seed + SPProtoEncoder_RemoveOTPSeed(&o->send_encoder); +} + +void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // add receiving seed + SPProtoDecoder_AddOTPSeed(&o->recv_decoder, seed_id, key, iv); +} + +void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remove receiving seeds + SPProtoDecoder_RemoveOTPSeeds(&o->recv_decoder); +} diff --git a/external/badvpn_dns/client/DatagramPeerIO.h b/external/badvpn_dns/client/DatagramPeerIO.h new file mode 100644 index 00000000..5d19b5ac --- /dev/null +++ b/external/badvpn_dns/client/DatagramPeerIO.h @@ -0,0 +1,271 @@ +/** + * @file DatagramPeerIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for comminicating with a peer using a datagram socket. + */ + +#ifndef BADVPN_CLIENT_DATAGRAMPEERIO_H +#define BADVPN_CLIENT_DATAGRAMPEERIO_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Callback function invoked when an error occurs with the peer connection. + * The object has entered default state. + * May be called from within a sending Send call. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_error) (void *user); + +/** + * Handler function invoked when the number of used OTPs has reached + * the specified warning number in {@link DatagramPeerIO_SetOTPWarningHandler}. + * May be called from within a sending Send call. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_otp_warning) (void *user); + +/** + * Handler called when OTP generation for a new receive seed is finished. + * + * @param user as in {@link DatagramPeerIO_SetHandlers} + */ +typedef void (*DatagramPeerIO_handler_otp_ready) (void *user); + +/** + * Object for comminicating with a peer using a datagram socket. + * + * The user provides data for sending to the peer through {@link PacketPassInterface}. + * Received data is provided to the user through {@link PacketPassInterface}. + * + * The object has a logical state called a mode, which is one of the following: + * - default - nothing is send or received + * - connecting - an address was provided by the user for sending datagrams to. + * Datagrams are being sent to that address through a socket, + * and datagrams are being received on the same socket. + * - binding - an address was provided by the user to bind a socket to. + * Datagrams are being received on the socket. Datagrams are not being + * sent initially. When a datagram is received, its source address is + * used as a destination address for sending datagrams. + */ +typedef struct { + DebugObject d_obj; + BReactor *reactor; + int payload_mtu; + struct spproto_security_params sp_params; + void *user; + BLog_logfunc logfunc; + DatagramPeerIO_handler_error handler_error; + int spproto_payload_mtu; + int effective_socket_mtu; + + // sending base + FragmentProtoDisassembler send_disassembler; + SPProtoEncoder send_encoder; + SinglePacketBuffer send_buffer; + PacketPassConnector send_connector; + + // receiving + PacketRecvConnector recv_connector; + SinglePacketBuffer recv_buffer; + SPProtoDecoder recv_decoder; + PacketPassNotifier recv_notifier; + FragmentProtoAssembler recv_assembler; + + // mode + int mode; + + // datagram object + BDatagram dgram; +} DatagramPeerIO; + +/** + * Initializes the object. + * The interface is initialized in default mode. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param reactor {@link BReactor} we live in + * @param payload_mtu maximum payload size. Must be >=0. + * @param socket_mtu maximum datagram size for the socket. Must be >=0. Must be large enough so it is possible to + * send a FragmentProto chunk with one byte of data over SPProto, i.e. the following has to hold: + * spproto_payload_mtu_for_carrier_mtu(sp_params, socket_mtu) > sizeof(struct fragmentproto_chunk_header) + * @param sp_params SPProto security parameters + * @param latency latency parameter to {@link FragmentProtoDisassembler_Init}. + * @param num_frames num_frames parameter to {@link FragmentProtoAssembler_Init}. Must be >0. + * @param recv_userif interface to pass received packets to the user. Its MTU must be >=payload_mtu. + * @param otp_warning_count If using OTPs, after how many encoded packets to call the handler. + * In this case, must be >0 and <=sp_params.otp_num. + * @param twd thread work dispatcher + * @param user value to pass to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @param handler_error error handler + * @param handler_otp_warning OTP warning handler + * @param handler_otp_ready handler called when OTP generation for a new receive seed is finished + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Init ( + DatagramPeerIO *o, + BReactor *reactor, + int payload_mtu, + int socket_mtu, + struct spproto_security_params sp_params, + btime_t latency, + int num_frames, + PacketPassInterface *recv_userif, + int otp_warning_count, + BThreadWorkDispatcher *twd, + void *user, + BLog_logfunc logfunc, + DatagramPeerIO_handler_error handler_error, + DatagramPeerIO_handler_otp_warning handler_otp_warning, + DatagramPeerIO_handler_otp_ready handler_otp_ready +) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void DatagramPeerIO_Free (DatagramPeerIO *o); + +/** + * Returns an interface the user should use to send packets. + * The OTP warning handler may be called from within Send calls + * to the interface. + * + * @param o the object + * @return sending interface + */ +PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o); + +/** + * Attempts to establish connection to the peer which has bound to an address. + * On success, the interface enters connecting mode. + * On failure, the interface enters default mode. + * + * @param o the object + * @param addr address to send packets to + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) WARN_UNUSED; + +/** + * Attempts to establish connection to the peer by binding to an address. + * On success, the interface enters connecting mode. + * On failure, the interface enters default mode. + * + * @param o the object + * @param addr address to bind to. Must be supported according to + * {@link BDatagram_AddressFamilySupported}. + * @return 1 on success, 0 on failure + */ +int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) WARN_UNUSED; + +/** + * Sets the encryption key to use for sending and receiving. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key); + +/** + * Removed the encryption key to use for sending and receiving. + * Encryption must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o); + +/** + * Sets the OTP seed for sending. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes the OTP seed for sending of one is configured. + * OTPs must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o); + +/** + * Adds an OTP seed for reciving. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all OTP seeds for reciving. + * OTPs must be enabled. + * + * @param o the object + */ +void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o); + +#endif diff --git a/external/badvpn_dns/client/FragmentProtoAssembler.c b/external/badvpn_dns/client/FragmentProtoAssembler.c new file mode 100644 index 00000000..8588c2e7 --- /dev/null +++ b/external/badvpn_dns/client/FragmentProtoAssembler.c @@ -0,0 +1,469 @@ +/** + * @file FragmentProtoAssembler.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "FragmentProtoAssembler.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#include "FragmentProtoAssembler_tree.h" +#include + +static void free_frame (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame) +{ + // remove from used list + LinkedList1_Remove(&o->frames_used, &frame->list_node); + // remove from used tree + FPAFramesTree_Remove(&o->frames_used_tree, 0, frame); + + // append to free list + LinkedList1_Append(&o->frames_free, &frame->list_node); +} + +static void free_oldest_frame (FragmentProtoAssembler *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->frames_used)) + + // obtain oldest frame (first on the list) + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used); + ASSERT(list_node) + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + + // free frame + free_frame(o, frame); +} + +static struct FragmentProtoAssembler_frame * allocate_new_frame (FragmentProtoAssembler *o, fragmentproto_frameid id) +{ + ASSERT(!FPAFramesTree_LookupExact(&o->frames_used_tree, 0, id)) + + // if there are no free entries, free the oldest used one + if (LinkedList1_IsEmpty(&o->frames_free)) { + PeerLog(o, BLOG_INFO, "freeing used frame"); + free_oldest_frame(o); + } + + // obtain frame entry + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_free); + ASSERT(list_node) + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + + // remove from free list + LinkedList1_Remove(&o->frames_free, &frame->list_node); + + // initialize values + frame->id = id; + frame->time = o->time; + frame->num_chunks = 0; + frame->sum = 0; + frame->length = -1; + frame->length_so_far = 0; + + // append to used list + LinkedList1_Append(&o->frames_used, &frame->list_node); + // insert to used tree + int res = FPAFramesTree_Insert(&o->frames_used_tree, 0, frame, NULL); + ASSERT_EXECUTE(res) + + return frame; +} + +static int chunks_overlap (int c1_start, int c1_len, int c2_start, int c2_len) +{ + return (c1_start + c1_len > c2_start && c2_start + c2_len > c1_start); +} + +static int frame_is_timed_out (FragmentProtoAssembler *o, struct FragmentProtoAssembler_frame *frame) +{ + ASSERT(frame->time <= o->time) + + return (o->time - frame->time > o->time_tolerance); +} + +static void reduce_times (FragmentProtoAssembler *o) +{ + // find the frame with minimal time, removing timed out frames + struct FragmentProtoAssembler_frame *minframe = NULL; + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->frames_used); + while (list_node) { + LinkedList1Node *next = LinkedList1Node_Next(list_node); + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + if (frame_is_timed_out(o, frame)) { + PeerLog(o, BLOG_INFO, "freeing timed out frame (while reducing times)"); + free_frame(o, frame); + } else { + if (!minframe || frame->time < minframe->time) { + minframe = frame; + } + } + list_node = next; + } + + if (!minframe) { + // have no frames, set packet time to zero + o->time = 0; + return; + } + + uint32_t min_time = minframe->time; + + // subtract minimal time from all frames + for (list_node = LinkedList1_GetFirst(&o->frames_used); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct FragmentProtoAssembler_frame *frame = UPPER_OBJECT(list_node, struct FragmentProtoAssembler_frame, list_node); + frame->time -= min_time; + } + + // subtract minimal time from packet time + o->time -= min_time; +} + +static int process_chunk (FragmentProtoAssembler *o, fragmentproto_frameid frame_id, int chunk_start, int chunk_len, int is_last, uint8_t *payload) +{ + ASSERT(chunk_start >= 0) + ASSERT(chunk_len >= 0) + ASSERT(is_last == 0 || is_last == 1) + + // verify chunk + + // check start + if (chunk_start > o->output_mtu) { + PeerLog(o, BLOG_INFO, "chunk starts outside"); + return 0; + } + + // check frame size bound + if (chunk_len > o->output_mtu - chunk_start) { + PeerLog(o, BLOG_INFO, "chunk ends outside"); + return 0; + } + + // calculate end + int chunk_end = chunk_start + chunk_len; + ASSERT(chunk_end >= 0) + ASSERT(chunk_end <= o->output_mtu) + + // lookup frame + struct FragmentProtoAssembler_frame *frame = FPAFramesTree_LookupExact(&o->frames_used_tree, 0, frame_id); + if (!frame) { + // frame not found, add a new one + frame = allocate_new_frame(o, frame_id); + } else { + // have existing frame with that ID + // check frame time + if (frame_is_timed_out(o, frame)) { + // frame is timed out, remove it and use a new one + PeerLog(o, BLOG_INFO, "freeing timed out frame (while processing chunk)"); + free_frame(o, frame); + frame = allocate_new_frame(o, frame_id); + } + } + + ASSERT(frame->num_chunks < o->num_chunks) + + // check if the chunk overlaps with any existing chunks + for (int i = 0; i < frame->num_chunks; i++) { + struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[i]; + if (chunks_overlap(chunk->start, chunk->len, chunk_start, chunk_len)) { + PeerLog(o, BLOG_INFO, "chunk overlaps with existing chunk"); + goto fail_frame; + } + } + + if (is_last) { + // this chunk is marked as last + if (frame->length >= 0) { + PeerLog(o, BLOG_INFO, "got last chunk, but already have one"); + goto fail_frame; + } + // check if frame size according to this packet is consistent + // with existing chunks + if (frame->length_so_far > chunk_end) { + PeerLog(o, BLOG_INFO, "got last chunk, but already have data over its bound"); + goto fail_frame; + } + } else { + // if we have length, chunk must be in its bound + if (frame->length >= 0) { + if (chunk_end > frame->length) { + PeerLog(o, BLOG_INFO, "chunk out of length bound"); + goto fail_frame; + } + } + } + + // chunk is good, add it + + // update frame time + frame->time = o->time; + + // add chunk entry + struct FragmentProtoAssembler_chunk *chunk = &frame->chunks[frame->num_chunks]; + chunk->start = chunk_start; + chunk->len = chunk_len; + frame->num_chunks++; + + // update sum + frame->sum += chunk_len; + + // update length + if (is_last) { + frame->length = chunk_end; + } else { + if (frame->length < 0) { + if (frame->length_so_far < chunk_end) { + frame->length_so_far = chunk_end; + } + } + } + + // copy chunk payload to buffer + memcpy(frame->buffer + chunk_start, payload, chunk_len); + + // is frame incomplete? + if (frame->length < 0 || frame->sum < frame->length) { + // if all chunks are used, fail it + if (frame->num_chunks == o->num_chunks) { + PeerLog(o, BLOG_INFO, "all chunks used, but frame not complete"); + goto fail_frame; + } + + // wait for more chunks + return 0; + } + + ASSERT(frame->sum == frame->length) + + PeerLog(o, BLOG_DEBUG, "frame complete"); + + // free frame entry + free_frame(o, frame); + + // send frame + PacketPassInterface_Sender_Send(o->output, frame->buffer, frame->length); + + return 1; + +fail_frame: + free_frame(o, frame); + return 0; +} + +static void process_input (FragmentProtoAssembler *o) +{ + ASSERT(o->in_len >= 0) + + // read chunks + while (o->in_pos < o->in_len) { + // obtain header + if (o->in_len - o->in_pos < sizeof(struct fragmentproto_chunk_header)) { + PeerLog(o, BLOG_INFO, "too little data for chunk header"); + break; + } + struct fragmentproto_chunk_header header; + memcpy(&header, o->in + o->in_pos, sizeof(header)); + o->in_pos += sizeof(struct fragmentproto_chunk_header); + fragmentproto_frameid frame_id = ltoh16(header.frame_id); + int chunk_start = ltoh16(header.chunk_start); + int chunk_len = ltoh16(header.chunk_len); + int is_last = ltoh8(header.is_last); + + // check is_last field + if (!(is_last == 0 || is_last == 1)) { + PeerLog(o, BLOG_INFO, "chunk is_last wrong"); + break; + } + + // obtain data + if (o->in_len - o->in_pos < chunk_len) { + PeerLog(o, BLOG_INFO, "too little data for chunk data"); + break; + } + + // process chunk + int res = process_chunk(o, frame_id, chunk_start, chunk_len, is_last, o->in + o->in_pos); + o->in_pos += chunk_len; + + if (res) { + // sending complete frame, stop processing input + return; + } + } + + // increment packet time + if (o->time == FPA_MAX_TIME) { + reduce_times(o); + if (!LinkedList1_IsEmpty(&o->frames_used)) { + ASSERT(o->time < FPA_MAX_TIME) // If there was a frame with zero time, it was removed because + // time_tolerance < FPA_MAX_TIME. So something >0 was subtracted. + o->time++; + } else { + // it was set to zero by reduce_times + ASSERT(o->time == 0) + } + } else { + o->time++; + } + + // set no input packet + o->in_len = -1; + + // finish input + PacketPassInterface_Done(&o->input); +} + +static void input_handler_send (FragmentProtoAssembler *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // save input packet + o->in_len = data_len; + o->in = data; + o->in_pos = 0; + + process_input(o); +} + +static void output_handler_done (FragmentProtoAssembler *o) +{ + ASSERT(o->in_len >= 0) + DebugObject_Access(&o->d_obj); + + process_input(o); +} + +int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc) +{ + ASSERT(input_mtu >= 0) + ASSERT(num_frames > 0) + ASSERT(num_frames < FPA_MAX_TIME) // needed so we can always subtract times when packet time is maximum + ASSERT(num_chunks > 0) + + // init arguments + o->output = output; + o->num_chunks = num_chunks; + o->user = user; + o->logfunc = logfunc; + + // init input + PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // remebmer output MTU + o->output_mtu = PacketPassInterface_GetMTU(o->output); + + // set packet time to zero + o->time = 0; + + // set time tolerance to num_frames + o->time_tolerance = num_frames; + + // allocate frames + if (!(o->frames_entries = (struct FragmentProtoAssembler_frame *)BAllocArray(num_frames, sizeof(o->frames_entries[0])))) { + goto fail1; + } + + // allocate chunks + if (!(o->frames_chunks = (struct FragmentProtoAssembler_chunk *)BAllocArray2(num_frames, o->num_chunks, sizeof(o->frames_chunks[0])))) { + goto fail2; + } + + // allocate buffers + if (!(o->frames_buffer = (uint8_t *)BAllocArray(num_frames, o->output_mtu))) { + goto fail3; + } + + // init frame lists + LinkedList1_Init(&o->frames_free); + LinkedList1_Init(&o->frames_used); + + // initialize frame entries + for (int i = 0; i < num_frames; i++) { + struct FragmentProtoAssembler_frame *frame = &o->frames_entries[i]; + // set chunks array pointer + frame->chunks = o->frames_chunks + (size_t)i * o->num_chunks; + // set buffer pointer + frame->buffer = o->frames_buffer + (size_t)i * o->output_mtu; + // add to free list + LinkedList1_Append(&o->frames_free, &frame->list_node); + } + + // init tree + FPAFramesTree_Init(&o->frames_used_tree); + + // have no input packet + o->in_len = -1; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail3: + BFree(o->frames_chunks); +fail2: + BFree(o->frames_entries); +fail1: + PacketPassInterface_Free(&o->input); + return 0; +} + +void FragmentProtoAssembler_Free (FragmentProtoAssembler *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffers + BFree(o->frames_buffer); + + // free chunks + BFree(o->frames_chunks); + + // free frames + BFree(o->frames_entries); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/external/badvpn_dns/client/FragmentProtoAssembler.h b/external/badvpn_dns/client/FragmentProtoAssembler.h new file mode 100644 index 00000000..bbc5483a --- /dev/null +++ b/external/badvpn_dns/client/FragmentProtoAssembler.h @@ -0,0 +1,134 @@ +/** + * @file FragmentProtoAssembler.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes packets according to FragmentProto. + */ + +#ifndef BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H +#define BADVPN_CLIENT_FRAGMENTPROTOASSEMBLER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPA_MAX_TIME UINT32_MAX + +struct FragmentProtoAssembler_frame; + +#include "FragmentProtoAssembler_tree.h" +#include + +struct FragmentProtoAssembler_chunk { + int start; + int len; +}; + +struct FragmentProtoAssembler_frame { + LinkedList1Node list_node; // node in free or used list + struct FragmentProtoAssembler_chunk *chunks; // array of chunks, up to num_chunks + uint8_t *buffer; // buffer with frame data, size output_mtu + // everything below only defined when frame entry is used + fragmentproto_frameid id; // frame identifier + uint32_t time; // packet time when the last chunk was received + FPAFramesTreeNode tree_node; // node fields in tree for searching frames by id + int num_chunks; // number of valid chunks + int sum; // sum of all chunks' lengths + int length; // length of the frame, or -1 if not yet known + int length_so_far; // if length=-1, current data set's upper bound +}; + +/** + * Object which decodes packets according to FragmentProto. + * + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + void *user; + BLog_logfunc logfunc; + PacketPassInterface input; + PacketPassInterface *output; + int output_mtu; + int num_chunks; + uint32_t time; + int time_tolerance; + struct FragmentProtoAssembler_frame *frames_entries; + struct FragmentProtoAssembler_chunk *frames_chunks; + uint8_t *frames_buffer; + LinkedList1 frames_free; + LinkedList1 frames_used; + FPAFramesTree frames_used_tree; + int in_len; + uint8_t *in; + int in_pos; + DebugObject d_obj; +} FragmentProtoAssembler; + +/** + * Initializes the object. + * {@link BLog_Init} must have been done. + * + * @param o the object + * @param input_mtu maximum input packet size. Must be >=0. + * @param output output interface + * @param num_frames number of frames we can hold. Must be >0 and < FPA_MAX_TIME. + * To make the assembler tolerate out-of-order input of degree D, set to D+2. + * Here, D is the minimum size of a hypothetical buffer needed to order the input. + * @param num_chunks maximum number of chunks a frame can come in. Must be >0. + * @param pg pending group + * @param user argument to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int FragmentProtoAssembler_Init (FragmentProtoAssembler *o, int input_mtu, PacketPassInterface *output, int num_frames, int num_chunks, BPendingGroup *pg, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void FragmentProtoAssembler_Free (FragmentProtoAssembler *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * FragmentProtoAssembler_GetInput (FragmentProtoAssembler *o); + +#endif diff --git a/external/badvpn_dns/client/FragmentProtoAssembler_tree.h b/external/badvpn_dns/client/FragmentProtoAssembler_tree.h new file mode 100644 index 00000000..744c6339 --- /dev/null +++ b/external/badvpn_dns/client/FragmentProtoAssembler_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FPAFramesTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct FragmentProtoAssembler_frame +#define SAVL_PARAM_TYPE_KEY fragmentproto_frameid +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->id, (entry2)->id) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->id) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/external/badvpn_dns/client/FragmentProtoDisassembler.c b/external/badvpn_dns/client/FragmentProtoDisassembler.c new file mode 100644 index 00000000..e67a1dc0 --- /dev/null +++ b/external/badvpn_dns/client/FragmentProtoDisassembler.c @@ -0,0 +1,229 @@ +/** + * @file FragmentProtoDisassembler.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "client/FragmentProtoDisassembler.h" + +static void write_chunks (FragmentProtoDisassembler *o) +{ + #define IN_AVAIL (o->in_len - o->in_used) + #define OUT_AVAIL ((o->output_mtu - o->out_used) - (int)sizeof(struct fragmentproto_chunk_header)) + + ASSERT(o->in_len >= 0) + ASSERT(o->out) + ASSERT(OUT_AVAIL > 0) + + // write chunks to output packet + do { + // calculate chunk length + int chunk_len = bmin_int(IN_AVAIL, OUT_AVAIL); + if (o->chunk_mtu > 0) { + chunk_len = bmin_int(chunk_len, o->chunk_mtu); + } + + // write chunk header + struct fragmentproto_chunk_header header; + header.frame_id = htol16(o->frame_id); + header.chunk_start = htol16(o->in_used); + header.chunk_len = htol16(chunk_len); + header.is_last = (chunk_len == IN_AVAIL); + memcpy(o->out + o->out_used, &header, sizeof(header)); + + // write chunk data + memcpy(o->out + o->out_used + sizeof(struct fragmentproto_chunk_header), o->in + o->in_used, chunk_len); + + // increment pointers + o->in_used += chunk_len; + o->out_used += sizeof(struct fragmentproto_chunk_header) + chunk_len; + } while (IN_AVAIL > 0 && OUT_AVAIL > 0); + + // have we finished the input packet? + if (IN_AVAIL == 0) { + // set no input packet + o->in_len = -1; + + // increment frame ID + o->frame_id++; + + // finish input + PacketPassInterface_Done(&o->input); + } + + // should we finish the output packet? + if (OUT_AVAIL <= 0 || o->latency < 0) { + // set no output packet + o->out = NULL; + + // stop timer (if it's running) + if (o->latency >= 0) { + BReactor_RemoveTimer(o->reactor, &o->timer); + } + + // finish output + PacketRecvInterface_Done(&o->output, o->out_used); + } else { + // start timer if we have output and it's not running (output was empty before) + if (!BTimer_IsRunning(&o->timer)) { + BReactor_SetTimer(o->reactor, &o->timer); + } + } +} + +static void input_handler_send (FragmentProtoDisassembler *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(o->in_len == -1) + + // set input packet + o->in_len = data_len; + o->in = data; + o->in_used = 0; + + // if there is no output, wait for it + if (!o->out) { + return; + } + + write_chunks(o); +} + +static void input_handler_requestcancel (FragmentProtoDisassembler *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->out) + + // set no input packet + o->in_len = -1; + + // finish input + PacketPassInterface_Done(&o->input); +} + +static void output_handler_recv (FragmentProtoDisassembler *o, uint8_t *data) +{ + ASSERT(data) + ASSERT(!o->out) + + // set output packet + o->out = data; + o->out_used = 0; + + // if there is no input, wait for it + if (o->in_len < 0) { + return; + } + + write_chunks(o); +} + +static void timer_handler (FragmentProtoDisassembler *o) +{ + ASSERT(o->latency >= 0) + ASSERT(o->out) + ASSERT(o->in_len == -1) + + // set no output packet + o->out = NULL; + + // finish output + PacketRecvInterface_Done(&o->output, o->out_used); +} + +void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency) +{ + ASSERT(input_mtu >= 0) + ASSERT(input_mtu <= UINT16_MAX) + ASSERT(output_mtu > sizeof(struct fragmentproto_chunk_header)) + ASSERT(chunk_mtu > 0 || chunk_mtu < 0) + + // init arguments + o->reactor = reactor; + o->output_mtu = output_mtu; + o->chunk_mtu = chunk_mtu; + o->latency = latency; + + // init input + PacketPassInterface_Init(&o->input, input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(reactor)); + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(reactor)); + + // init timer + if (o->latency >= 0) { + BTimer_Init(&o->timer, o->latency, (BTimer_handler)timer_handler, o); + } + + // have no input packet + o->in_len = -1; + + // have no output packet + o->out = NULL; + + // start with zero frame ID + o->frame_id = 0; + + DebugObject_Init(&o->d_obj); +} + +void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o) +{ + DebugObject_Free(&o->d_obj); + + // free timer + if (o->latency >= 0) { + BReactor_RemoveTimer(o->reactor, &o->timer); + } + + // free output + PacketRecvInterface_Free(&o->output); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/client/FragmentProtoDisassembler.h b/external/badvpn_dns/client/FragmentProtoDisassembler.h new file mode 100644 index 00000000..49fe9c89 --- /dev/null +++ b/external/badvpn_dns/client/FragmentProtoDisassembler.h @@ -0,0 +1,109 @@ +/** + * @file FragmentProtoDisassembler.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets into packets composed of chunks + * according to FragmentProto. + */ + +#ifndef BADVPN_CLIENT_CCPROTODISASSEMBLER_H +#define BADVPN_CLIENT_CCPROTODISASSEMBLER_H + +#include + +#include +#include +#include +#include +#include +#include + +/** + * Object which encodes packets into packets composed of chunks + * according to FragmentProto. + * + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + BReactor *reactor; + int output_mtu; + int chunk_mtu; + btime_t latency; + PacketPassInterface input; + PacketRecvInterface output; + BTimer timer; + int in_len; + uint8_t *in; + int in_used; + uint8_t *out; + int out_used; + fragmentproto_frameid frame_id; + DebugObject d_obj; +} FragmentProtoDisassembler; + +/** + * Initializes the object. + * + * @param o the object + * @param reactor reactor we live in + * @param input_mtu maximum input packet size. Must be >=0 and <=UINT16_MAX. + * @param output_mtu maximum output packet size. Must be >sizeof(struct fragmentproto_chunk_header). + * @param chunk_mtu maximum chunk size. Must be >0, or <0 for no explicit limit. + * @param latency maximum time a pending output packet with some data can wait for more data + * before being sent out. If nonnegative, a timer will be used. If negative, + * packets will always be sent out immediately. If low latency is desired, + * prefer setting this to zero rather than negative. + */ +void FragmentProtoDisassembler_Init (FragmentProtoDisassembler *o, BReactor *reactor, int input_mtu, int output_mtu, int chunk_mtu, btime_t latency); + +/** + * Frees the object. + * + * @param o the object + */ +void FragmentProtoDisassembler_Free (FragmentProtoDisassembler *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * FragmentProtoDisassembler_GetInput (FragmentProtoDisassembler *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * FragmentProtoDisassembler_GetOutput (FragmentProtoDisassembler *o); + +#endif diff --git a/external/badvpn_dns/client/FrameDecider.c b/external/badvpn_dns/client/FrameDecider.c new file mode 100644 index 00000000..e7bb4ded --- /dev/null +++ b/external/badvpn_dns/client/FrameDecider.c @@ -0,0 +1,795 @@ +/** + * @file FrameDecider.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DECIDE_STATE_NONE 1 +#define DECIDE_STATE_UNICAST 2 +#define DECIDE_STATE_FLOOD 3 +#define DECIDE_STATE_MULTICAST 4 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static int compare_macs (const uint8_t *mac1, const uint8_t *mac2) +{ + int c = memcmp(mac1, mac2, 6); + return B_COMPARE(c, 0); +} + +#include "FrameDecider_macs_tree.h" +#include + +#include "FrameDecider_groups_tree.h" +#include + +#include "FrameDecider_multicast_tree.h" +#include + +static void add_mac_to_peer (FrameDeciderPeer *o, uint8_t *mac) +{ + FrameDecider *d = o->d; + + // locate entry in tree + struct _FrameDecider_mac_entry *e_entry = FDMacsTree_LookupExact(&d->macs_tree, 0, mac); + if (e_entry) { + if (e_entry->peer == o) { + // this is our MAC; only move it to the end of the used list + LinkedList1_Remove(&o->mac_entries_used, &e_entry->list_node); + LinkedList1_Append(&o->mac_entries_used, &e_entry->list_node); + return; + } + + // some other peer has that MAC; disassociate it + FDMacsTree_Remove(&d->macs_tree, 0, e_entry); + LinkedList1_Remove(&e_entry->peer->mac_entries_used, &e_entry->list_node); + LinkedList1_Append(&e_entry->peer->mac_entries_free, &e_entry->list_node); + } + + // aquire MAC address entry, if there are no free ones reuse the oldest used one + LinkedList1Node *list_node; + struct _FrameDecider_mac_entry *entry; + if (list_node = LinkedList1_GetFirst(&o->mac_entries_free)) { + entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node); + ASSERT(entry->peer == o) + + // remove from free + LinkedList1_Remove(&o->mac_entries_free, &entry->list_node); + } else { + list_node = LinkedList1_GetFirst(&o->mac_entries_used); + ASSERT(list_node) + entry = UPPER_OBJECT(list_node, struct _FrameDecider_mac_entry, list_node); + ASSERT(entry->peer == o) + + // remove from used + FDMacsTree_Remove(&d->macs_tree, 0, entry); + LinkedList1_Remove(&o->mac_entries_used, &entry->list_node); + } + + PeerLog(o, BLOG_INFO, "adding MAC %02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8"", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + // set MAC in entry + memcpy(entry->mac, mac, sizeof(entry->mac)); + + // add to used + LinkedList1_Append(&o->mac_entries_used, &entry->list_node); + int res = FDMacsTree_Insert(&d->macs_tree, 0, entry, NULL); + ASSERT_EXECUTE(res) +} + +static uint32_t compute_sig_for_group (uint32_t group) +{ + return hton32(ntoh32(group)&0x7FFFFF); +} + +static uint32_t compute_sig_for_mac (uint8_t *mac) +{ + uint32_t sig; + memcpy(&sig, mac + 2, 4); + sig = hton32(ntoh32(sig)&0x7FFFFF); + return sig; +} + +static void add_to_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry) +{ + // compute sig + uint32_t sig = compute_sig_for_group(group_entry->group); + + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig); + if (master) { + // use existing master + ASSERT(master->is_master) + + // set not master + group_entry->is_master = 0; + + // insert to list + LinkedList3Node_InitAfter(&group_entry->sig_list_node, &master->sig_list_node); + } else { + // make this entry master + + // set master + group_entry->is_master = 1; + + // set sig + group_entry->master.sig = sig; + + // insert to multicast tree + int res = FDMulticastTree_Insert(&d->multicast_tree, 0, group_entry, NULL); + ASSERT_EXECUTE(res) + + // init list node + LinkedList3Node_InitLonely(&group_entry->sig_list_node); + } +} + +static void remove_from_multicast (FrameDecider *d, struct _FrameDecider_group_entry *group_entry) +{ + // compute sig + uint32_t sig = compute_sig_for_group(group_entry->group); + + if (group_entry->is_master) { + // remove master from multicast tree + FDMulticastTree_Remove(&d->multicast_tree, 0, group_entry); + + if (!LinkedList3Node_IsLonely(&group_entry->sig_list_node)) { + // at least one more group entry for this sig; make another entry the master + + // get an entry + LinkedList3Node *list_node = LinkedList3Node_NextOrPrev(&group_entry->sig_list_node); + struct _FrameDecider_group_entry *newmaster = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node); + ASSERT(!newmaster->is_master) + + // set master + newmaster->is_master = 1; + + // set sig + newmaster->master.sig = sig; + + // insert to multicast tree + int res = FDMulticastTree_Insert(&d->multicast_tree, 0, newmaster, NULL); + ASSERT_EXECUTE(res) + } + } + + // free linked list node + LinkedList3Node_Free(&group_entry->sig_list_node); +} + +static void add_group_to_peer (FrameDeciderPeer *o, uint32_t group) +{ + FrameDecider *d = o->d; + + struct _FrameDecider_group_entry *group_entry = FDGroupsTree_LookupExact(&o->groups_tree, 0, group); + if (group_entry) { + // move to end of used list + LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node); + LinkedList1_Append(&o->group_entries_used, &group_entry->list_node); + } else { + PeerLog(o, BLOG_INFO, "joined group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", + ((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3] + ); + + // aquire group entry, if there are no free ones reuse the earliest used one + LinkedList1Node *node; + if (node = LinkedList1_GetFirst(&o->group_entries_free)) { + group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from free list + LinkedList1_Remove(&o->group_entries_free, &group_entry->list_node); + } else { + node = LinkedList1_GetFirst(&o->group_entries_used); + ASSERT(node) + group_entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from multicast + remove_from_multicast(d, group_entry); + + // remove from peer's groups tree + FDGroupsTree_Remove(&o->groups_tree, 0, group_entry); + + // remove from used list + LinkedList1_Remove(&o->group_entries_used, &group_entry->list_node); + } + + // add entry to used list + LinkedList1_Append(&o->group_entries_used, &group_entry->list_node); + + // set group address + group_entry->group = group; + + // insert to peer's groups tree + int res = FDGroupsTree_Insert(&o->groups_tree, 0, group_entry, NULL); + ASSERT_EXECUTE(res) + + // add to multicast + add_to_multicast(d, group_entry); + } + + // set timer + group_entry->timer_endtime = btime_gettime() + d->igmp_group_membership_interval; + BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime); +} + +static void remove_group_entry (struct _FrameDecider_group_entry *group_entry) +{ + FrameDeciderPeer *peer = group_entry->peer; + FrameDecider *d = peer->d; + + uint32_t group = group_entry->group; + + PeerLog(peer, BLOG_INFO, "left group %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", + ((uint8_t *)&group)[0], ((uint8_t *)&group)[1], ((uint8_t *)&group)[2], ((uint8_t *)&group)[3] + ); + + // remove from multicast + remove_from_multicast(d, group_entry); + + // remove from peer's groups tree + FDGroupsTree_Remove(&peer->groups_tree, 0, group_entry); + + // remove from used list + LinkedList1_Remove(&peer->group_entries_used, &group_entry->list_node); + + // add to free list + LinkedList1_Append(&peer->group_entries_free, &group_entry->list_node); + + // stop timer + BReactor_RemoveTimer(d->reactor, &group_entry->timer); +} + +static void lower_group_timers_to_lmqt (FrameDecider *d, uint32_t group) +{ + // have to lower all the group timers of this group down to LMQT + + // compute sig + uint32_t sig = compute_sig_for_group(group); + + // look up the sig in multicast tree + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&d->multicast_tree, 0, sig); + if (!master) { + return; + } + ASSERT(master->is_master) + + // iterate all group entries with this sig + LinkedList3Iterator it; + LinkedList3Iterator_Init(&it, LinkedList3Node_First(&master->sig_list_node), 1); + LinkedList3Node *sig_list_node; + while (sig_list_node = LinkedList3Iterator_Next(&it)) { + struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(sig_list_node, struct _FrameDecider_group_entry, sig_list_node); + + // skip wrong groups + if (group_entry->group != group) { + continue; + } + + // lower timer down to LMQT + btime_t now = btime_gettime(); + if (group_entry->timer_endtime > now + d->igmp_last_member_query_time) { + group_entry->timer_endtime = now + d->igmp_last_member_query_time; + BReactor_SetTimerAbsolute(d->reactor, &group_entry->timer, group_entry->timer_endtime); + } + } +} + +static void group_entry_timer_handler (struct _FrameDecider_group_entry *group_entry) +{ + DebugObject_Access(&group_entry->peer->d_obj); + + remove_group_entry(group_entry); +} + +void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor) +{ + ASSERT(max_peer_macs > 0) + ASSERT(max_peer_groups > 0) + + // init arguments + o->max_peer_macs = max_peer_macs; + o->max_peer_groups = max_peer_groups; + o->igmp_group_membership_interval = igmp_group_membership_interval; + o->igmp_last_member_query_time = igmp_last_member_query_time; + o->reactor = reactor; + + // init peers list + LinkedList1_Init(&o->peers_list); + + // init MAC tree + FDMacsTree_Init(&o->macs_tree); + + // init multicast tree + FDMulticastTree_Init(&o->multicast_tree); + + // init decide state + o->decide_state = DECIDE_STATE_NONE; + + // set no current flood peer + o->decide_flood_current = NULL; + + DebugObject_Init(&o->d_obj); +} + +void FrameDecider_Free (FrameDecider *o) +{ + ASSERT(FDMulticastTree_IsEmpty(&o->multicast_tree)) + ASSERT(FDMacsTree_IsEmpty(&o->macs_tree)) + ASSERT(LinkedList1_IsEmpty(&o->peers_list)) + DebugObject_Free(&o->d_obj); +} + +void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + DebugObject_Access(&o->d_obj); + + // reset decide state + switch (o->decide_state) { + case DECIDE_STATE_NONE: + break; + case DECIDE_STATE_UNICAST: + break; + case DECIDE_STATE_FLOOD: + break; + case DECIDE_STATE_MULTICAST: + LinkedList3Iterator_Free(&o->decide_multicast_it); + return; + default: + ASSERT(0); + } + o->decide_state = DECIDE_STATE_NONE; + o->decide_flood_current = NULL; + + // analyze frame + + const uint8_t *pos = frame; + int len = frame_len; + + if (len < sizeof(struct ethernet_header)) { + return; + } + struct ethernet_header eh; + memcpy(&eh, pos, sizeof(eh)); + pos += sizeof(struct ethernet_header); + len -= sizeof(struct ethernet_header); + + int is_igmp = 0; + + switch (ntoh16(eh.type)) { + case ETHERTYPE_IPV4: { + // check IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) { + BLog(BLOG_INFO, "decide: wrong IP packet"); + goto out; + } + + // check if it's IGMP + if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) { + goto out; + } + + // remember that it's IGMP; we have to flood IGMP frames + is_igmp = 1; + + // check IGMP header + if (len < sizeof(struct igmp_base)) { + BLog(BLOG_INFO, "decide: IGMP: short packet"); + goto out; + } + struct igmp_base igmp_base; + memcpy(&igmp_base, pos, sizeof(igmp_base)); + pos += sizeof(struct igmp_base); + len -= sizeof(struct igmp_base); + + switch (ntoh8(igmp_base.type)) { + case IGMP_TYPE_MEMBERSHIP_QUERY: { + if (len == sizeof(struct igmp_v2_extra) && ntoh8(igmp_base.max_resp_code) != 0) { + // V2 query + struct igmp_v2_extra query; + memcpy(&query, pos, sizeof(query)); + pos += sizeof(struct igmp_v2_extra); + len -= sizeof(struct igmp_v2_extra); + + if (ntoh32(query.group) != 0) { + // got a Group-Specific Query, lower group timers to LMQT + lower_group_timers_to_lmqt(o, query.group); + } + } + else if (len >= sizeof(struct igmp_v3_query_extra)) { + // V3 query + struct igmp_v3_query_extra query; + memcpy(&query, pos, sizeof(query)); + pos += sizeof(struct igmp_v3_query_extra); + len -= sizeof(struct igmp_v3_query_extra); + + // iterate sources + uint16_t num_sources = ntoh16(query.number_of_sources); + int i; + for (i = 0; i < num_sources; i++) { + // check source + if (len < sizeof(struct igmp_source)) { + BLog(BLOG_NOTICE, "decide: IGMP: short source"); + goto out; + } + pos += sizeof(struct igmp_source); + len -= sizeof(struct igmp_source); + } + + if (ntoh32(query.group) != 0 && num_sources == 0) { + // got a Group-Specific Query, lower group timers to LMQT + lower_group_timers_to_lmqt(o, query.group); + } + } + } break; + } + } break; + } + +out:; + + const uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const uint8_t multicast_mac_header[] = {0x01, 0x00, 0x5e}; + + // if it's broadcast or IGMP, flood it + if (is_igmp || !memcmp(eh.dest, broadcast_mac, sizeof(broadcast_mac))) { + o->decide_state = DECIDE_STATE_FLOOD; + o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list); + return; + } + + // if it's multicast, forward to all peers with the given sig + if (!memcmp(eh.dest, multicast_mac_header, sizeof(multicast_mac_header))) { + // extract group's sig from destination MAC + uint32_t sig = compute_sig_for_mac(eh.dest); + + // look up the sig in multicast tree + struct _FrameDecider_group_entry *master = FDMulticastTree_LookupExact(&o->multicast_tree, 0, sig); + if (master) { + ASSERT(master->is_master) + + o->decide_state = DECIDE_STATE_MULTICAST; + LinkedList3Iterator_Init(&o->decide_multicast_it, LinkedList3Node_First(&master->sig_list_node), 1); + } + + return; + } + + // look for MAC entry + struct _FrameDecider_mac_entry *entry = FDMacsTree_LookupExact(&o->macs_tree, 0, eh.dest); + if (entry) { + o->decide_state = DECIDE_STATE_UNICAST; + o->decide_unicast_peer = entry->peer; + return; + } + + // unknown destination MAC, flood + o->decide_state = DECIDE_STATE_FLOOD; + o->decide_flood_current = LinkedList1_GetFirst(&o->peers_list); + return; +} + +FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o) +{ + DebugObject_Access(&o->d_obj); + + switch (o->decide_state) { + case DECIDE_STATE_NONE: { + return NULL; + } break; + + case DECIDE_STATE_UNICAST: { + o->decide_state = DECIDE_STATE_NONE; + + return o->decide_unicast_peer; + } break; + + case DECIDE_STATE_FLOOD: { + if (!o->decide_flood_current) { + o->decide_state = DECIDE_STATE_NONE; + return NULL; + } + + LinkedList1Node *list_node = o->decide_flood_current; + o->decide_flood_current = LinkedList1Node_Next(o->decide_flood_current); + + FrameDeciderPeer *peer = UPPER_OBJECT(list_node, FrameDeciderPeer, list_node); + + return peer; + } break; + + case DECIDE_STATE_MULTICAST: { + LinkedList3Node *list_node = LinkedList3Iterator_Next(&o->decide_multicast_it); + if (!list_node) { + o->decide_state = DECIDE_STATE_NONE; + return NULL; + } + struct _FrameDecider_group_entry *group_entry = UPPER_OBJECT(list_node, struct _FrameDecider_group_entry, sig_list_node); + + return group_entry->peer; + } break; + + default: + ASSERT(0); + return NULL; + } +} + +int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc) +{ + // init arguments + o->d = d; + o->user = user; + o->logfunc = logfunc; + + // allocate MAC entries + if (!(o->mac_entries = (struct _FrameDecider_mac_entry *)BAllocArray(d->max_peer_macs, sizeof(struct _FrameDecider_mac_entry)))) { + PeerLog(o, BLOG_ERROR, "failed to allocate MAC entries"); + goto fail0; + } + + // allocate group entries + if (!(o->group_entries = (struct _FrameDecider_group_entry *)BAllocArray(d->max_peer_groups, sizeof(struct _FrameDecider_group_entry)))) { + PeerLog(o, BLOG_ERROR, "failed to allocate group entries"); + goto fail1; + } + + // insert to peers list + LinkedList1_Append(&d->peers_list, &o->list_node); + + // init MAC entry lists + LinkedList1_Init(&o->mac_entries_free); + LinkedList1_Init(&o->mac_entries_used); + + // initialize MAC entries + for (int i = 0; i < d->max_peer_macs; i++) { + struct _FrameDecider_mac_entry *entry = &o->mac_entries[i]; + + // set peer + entry->peer = o; + + // insert to free list + LinkedList1_Append(&o->mac_entries_free, &entry->list_node); + } + + // init group entry lists + LinkedList1_Init(&o->group_entries_free); + LinkedList1_Init(&o->group_entries_used); + + // initialize group entries + for (int i = 0; i < d->max_peer_groups; i++) { + struct _FrameDecider_group_entry *entry = &o->group_entries[i]; + + // set peer + entry->peer = o; + + // insert to free list + LinkedList1_Append(&o->group_entries_free, &entry->list_node); + + // init timer + BTimer_Init(&entry->timer, 0, (BTimer_handler)group_entry_timer_handler, entry); + } + + // initialize groups tree + FDGroupsTree_Init(&o->groups_tree); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + BFree(o->mac_entries); +fail0: + return 0; +} + +void FrameDeciderPeer_Free (FrameDeciderPeer *o) +{ + DebugObject_Free(&o->d_obj); + + FrameDecider *d = o->d; + + // remove decide unicast reference + if (d->decide_state == DECIDE_STATE_UNICAST && d->decide_unicast_peer == o) { + d->decide_state = DECIDE_STATE_NONE; + } + + LinkedList1Node *node; + + // free group entries + for (node = LinkedList1_GetFirst(&o->group_entries_used); node; node = LinkedList1Node_Next(node)) { + struct _FrameDecider_group_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_group_entry, list_node); + + // remove from multicast + remove_from_multicast(d, entry); + + // stop timer + BReactor_RemoveTimer(d->reactor, &entry->timer); + } + + // remove used MAC entries from tree + for (node = LinkedList1_GetFirst(&o->mac_entries_used); node; node = LinkedList1Node_Next(node)) { + struct _FrameDecider_mac_entry *entry = UPPER_OBJECT(node, struct _FrameDecider_mac_entry, list_node); + + // remove from tree + FDMacsTree_Remove(&d->macs_tree, 0, entry); + } + + // remove from peers list + if (d->decide_flood_current == &o->list_node) { + d->decide_flood_current = LinkedList1Node_Next(d->decide_flood_current); + } + LinkedList1_Remove(&d->peers_list, &o->list_node); + + // free group entries + BFree(o->group_entries); + + // free MAC entries + BFree(o->mac_entries); +} + +void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + DebugObject_Access(&o->d_obj); + + const uint8_t *pos = frame; + int len = frame_len; + + if (len < sizeof(struct ethernet_header)) { + goto out; + } + struct ethernet_header eh; + memcpy(&eh, pos, sizeof(eh)); + pos += sizeof(struct ethernet_header); + len -= sizeof(struct ethernet_header); + + // register source MAC address with this peer + add_mac_to_peer(o, eh.source); + + switch (ntoh16(eh.type)) { + case ETHERTYPE_IPV4: { + // check IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check((uint8_t *)pos, len, &ipv4_header, (uint8_t **)&pos, &len)) { + PeerLog(o, BLOG_INFO, "analyze: wrong IP packet"); + goto out; + } + + // check if it's IGMP + if (ntoh8(ipv4_header.protocol) != IPV4_PROTOCOL_IGMP) { + goto out; + } + + // check IGMP header + if (len < sizeof(struct igmp_base)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short packet"); + goto out; + } + struct igmp_base igmp_base; + memcpy(&igmp_base, pos, sizeof(igmp_base)); + pos += sizeof(struct igmp_base); + len -= sizeof(struct igmp_base); + + switch (ntoh8(igmp_base.type)) { + case IGMP_TYPE_V2_MEMBERSHIP_REPORT: { + // check extra + if (len < sizeof(struct igmp_v2_extra)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short v2 report"); + goto out; + } + struct igmp_v2_extra report; + memcpy(&report, pos, sizeof(report)); + pos += sizeof(struct igmp_v2_extra); + len -= sizeof(struct igmp_v2_extra); + + // add to group + add_group_to_peer(o, report.group); + } break; + + case IGMP_TYPE_V3_MEMBERSHIP_REPORT: { + // check extra + if (len < sizeof(struct igmp_v3_report_extra)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short v3 report"); + goto out; + } + struct igmp_v3_report_extra report; + memcpy(&report, pos, sizeof(report)); + pos += sizeof(struct igmp_v3_report_extra); + len -= sizeof(struct igmp_v3_report_extra); + + // iterate records + uint16_t num_records = ntoh16(report.number_of_group_records); + for (int i = 0; i < num_records; i++) { + // check record + if (len < sizeof(struct igmp_v3_report_record)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short record header"); + goto out; + } + struct igmp_v3_report_record record; + memcpy(&record, pos, sizeof(record)); + pos += sizeof(struct igmp_v3_report_record); + len -= sizeof(struct igmp_v3_report_record); + + // iterate sources + uint16_t num_sources = ntoh16(record.number_of_sources); + int j; + for (j = 0; j < num_sources; j++) { + // check source + if (len < sizeof(struct igmp_source)) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short source"); + goto out; + } + pos += sizeof(struct igmp_source); + len -= sizeof(struct igmp_source); + } + + // check aux data + uint16_t aux_len = ntoh16(record.aux_data_len); + if (len < aux_len) { + PeerLog(o, BLOG_INFO, "analyze: IGMP: short record aux data"); + goto out; + } + pos += aux_len; + len -= aux_len; + + switch (record.type) { + case IGMP_RECORD_TYPE_MODE_IS_INCLUDE: + case IGMP_RECORD_TYPE_CHANGE_TO_INCLUDE_MODE: + if (num_sources != 0) { + add_group_to_peer(o, record.group); + } + break; + case IGMP_RECORD_TYPE_MODE_IS_EXCLUDE: + case IGMP_RECORD_TYPE_CHANGE_TO_EXCLUDE_MODE: + add_group_to_peer(o, record.group); + break; + } + } + } break; + } + } break; + } + +out:; +} diff --git a/external/badvpn_dns/client/FrameDecider.h b/external/badvpn_dns/client/FrameDecider.h new file mode 100644 index 00000000..f2a29372 --- /dev/null +++ b/external/badvpn_dns/client/FrameDecider.h @@ -0,0 +1,196 @@ +/** + * @file FrameDecider.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mudule which decides to which peers frames from the device are to be + * forwarded. + */ + +#ifndef BADVPN_CLIENT_FRAMEDECIDER_H +#define BADVPN_CLIENT_FRAMEDECIDER_H + +#include + +#include +#include +#include +#include +#include +#include + +struct _FrameDeciderPeer; +struct _FrameDecider_mac_entry; +struct _FrameDecider_group_entry; + +typedef const uint8_t *FDMacsTree_key; + +#include "FrameDecider_macs_tree.h" +#include + +#include "FrameDecider_groups_tree.h" +#include + +#include "FrameDecider_multicast_tree.h" +#include + +struct _FrameDecider_mac_entry { + struct _FrameDeciderPeer *peer; + LinkedList1Node list_node; // node in FrameDeciderPeer.mac_entries_free or FrameDeciderPeer.mac_entries_used + // defined when used: + uint8_t mac[6]; + FDMacsTreeNode tree_node; // node in FrameDecider.macs_tree, indexed by mac +}; + +struct _FrameDecider_group_entry { + struct _FrameDeciderPeer *peer; + LinkedList1Node list_node; // node in FrameDeciderPeer.group_entries_free or FrameDeciderPeer.group_entries_used + BTimer timer; // timer for removing the group entry, running when used + // defined when used: + // basic group data + uint32_t group; // group address + FDGroupsTreeNode tree_node; // node in FrameDeciderPeer.groups_tree, indexed by group + // all that folows is managed by add_to_multicast() and remove_from_multicast() + LinkedList3Node sig_list_node; // node in list of group entries with the same sig + btime_t timer_endtime; + int is_master; + // defined when used and we are master: + struct { + uint32_t sig; // last 23 bits of group address + FDMulticastTreeNode tree_node; // node in FrameDecider.multicast_tree, indexed by sig + } master; +}; + +/** + * Object that represents a local device. + */ +typedef struct { + int max_peer_macs; + int max_peer_groups; + btime_t igmp_group_membership_interval; + btime_t igmp_last_member_query_time; + BReactor *reactor; + LinkedList1 peers_list; + FDMacsTree macs_tree; + FDMulticastTree multicast_tree; + int decide_state; + LinkedList1Node *decide_flood_current; + struct _FrameDeciderPeer *decide_unicast_peer; + LinkedList3Iterator decide_multicast_it; + DebugObject d_obj; +} FrameDecider; + +/** + * Object that represents a peer that a local device can send frames to. + */ +typedef struct _FrameDeciderPeer { + FrameDecider *d; + void *user; + BLog_logfunc logfunc; + struct _FrameDecider_mac_entry *mac_entries; + struct _FrameDecider_group_entry *group_entries; + LinkedList1Node list_node; // node in FrameDecider.peers_list + LinkedList1 mac_entries_free; + LinkedList1 mac_entries_used; + LinkedList1 group_entries_free; + LinkedList1 group_entries_used; + FDGroupsTree groups_tree; + DebugObject d_obj; +} FrameDeciderPeer; + +/** + * Initializes the object. + * + * @param o the object + * @param max_peer_macs maximum number of MAC addresses a peer may posess. Must be >0. + * @param max_peer_groups maximum number of multicast groups a peer may belong to. Must be >0. + * @param igmp_group_membership_interval IGMP Group Membership Interval value. When a join + * is detected for a peer in {@link FrameDeciderPeer_Analyze}, this is how long we wait + * for another join before we remove the group from the peer. Note that the group may + * be removed sooner if the peer fails to respond to a Group-Specific Query (see below). + * @param igmp_last_member_query_time IGMP Last Member Query Time value. When a Group-Specific + * Query is detected in {@link FrameDecider_AnalyzeAndDecide}, this is how long we wait for a peer + * belonging to the group to send a join before we remove the group from it. + */ +void FrameDecider_Init (FrameDecider *o, int max_peer_macs, int max_peer_groups, btime_t igmp_group_membership_interval, btime_t igmp_last_member_query_time, BReactor *reactor); + +/** + * Frees the object. + * There must be no {@link FrameDeciderPeer} objects using this decider. + * + * @param o the object + */ +void FrameDecider_Free (FrameDecider *o); + +/** + * Analyzes a frame read from the local device and starts deciding which peers + * the frame should be forwarded to. + * + * @param o the object + * @param frame frame data + * @param frame_len frame length. Must be >=0. + */ +void FrameDecider_AnalyzeAndDecide (FrameDecider *o, const uint8_t *frame, int frame_len); + +/** + * Returns the next peer that the frame submitted to {@link FrameDecider_AnalyzeAndDecide} should be + * forwarded to. + * + * @param o the object + * @return peer to forward the frame to, or NULL if no more + */ +FrameDeciderPeer * FrameDecider_NextDestination (FrameDecider *o); + +/** + * Initializes the object. + * + * @param o the object + * @param d decider this peer will belong to + * @param user argument to log function + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int FrameDeciderPeer_Init (FrameDeciderPeer *o, FrameDecider *d, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void FrameDeciderPeer_Free (FrameDeciderPeer *o); + +/** + * Analyzes a frame received from the peer. + * + * @param o the object + * @param frame frame data + * @param frame_len frame length. Must be >=0. + */ +void FrameDeciderPeer_Analyze (FrameDeciderPeer *o, const uint8_t *frame, int frame_len); + +#endif diff --git a/external/badvpn_dns/client/FrameDecider_groups_tree.h b/external/badvpn_dns/client/FrameDecider_groups_tree.h new file mode 100644 index 00000000..b52a947b --- /dev/null +++ b/external/badvpn_dns/client/FrameDecider_groups_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDGroupsTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry +#define SAVL_PARAM_TYPE_KEY uint32_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->group, (entry2)->group) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->group) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/external/badvpn_dns/client/FrameDecider_macs_tree.h b/external/badvpn_dns/client/FrameDecider_macs_tree.h new file mode 100644 index 00000000..21459180 --- /dev/null +++ b/external/badvpn_dns/client/FrameDecider_macs_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDMacsTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_mac_entry +#define SAVL_PARAM_TYPE_KEY FDMacsTree_key +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_macs((entry1)->mac, (entry2)->mac) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) compare_macs((key1), (entry2)->mac) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/external/badvpn_dns/client/FrameDecider_multicast_tree.h b/external/badvpn_dns/client/FrameDecider_multicast_tree.h new file mode 100644 index 00000000..2731684a --- /dev/null +++ b/external/badvpn_dns/client/FrameDecider_multicast_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME FDMulticastTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct _FrameDecider_group_entry +#define SAVL_PARAM_TYPE_KEY uint32_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->master.sig, (entry2)->master.sig) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->master.sig) +#define SAVL_PARAM_MEMBER_NODE master.tree_node diff --git a/external/badvpn_dns/client/PasswordListener.c b/external/badvpn_dns/client/PasswordListener.c new file mode 100644 index 00000000..5ec573b6 --- /dev/null +++ b/external/badvpn_dns/client/PasswordListener.c @@ -0,0 +1,374 @@ +/** + * @file PasswordListener.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int password_comparator (void *user, uint64_t *p1, uint64_t *p2); +static void remove_client (struct PasswordListenerClient *client); +static void listener_handler (PasswordListener *l); +static void client_connection_handler (struct PasswordListenerClient *client, int event); +static void client_sslcon_handler (struct PasswordListenerClient *client, int event); +static void client_receiver_handler (struct PasswordListenerClient *client); + +int password_comparator (void *user, uint64_t *p1, uint64_t *p2) +{ + return B_COMPARE(*p1, *p2); +} + +void remove_client (struct PasswordListenerClient *client) +{ + PasswordListener *l = client->l; + + // stop using any buffers before they get freed + if (l->ssl) { + BSSLConnection_ReleaseBuffers(&client->sslcon); + } + + // free receiver + SingleStreamReceiver_Free(&client->receiver); + + // free SSL + if (l->ssl) { + BSSLConnection_Free(&client->sslcon); + ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS) + } + + // free connection interfaces + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + + // free connection + BConnection_Free(&client->sock->con); + + // free sslsocket structure + free(client->sock); + + // move to free list + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); +} + +void listener_handler (PasswordListener *l) +{ + DebugObject_Access(&l->d_obj); + + // obtain client entry + if (LinkedList1_IsEmpty(&l->clients_free)) { + struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetFirst(&l->clients_used), struct PasswordListenerClient, list_node); + remove_client(client); + } + struct PasswordListenerClient *client = UPPER_OBJECT(LinkedList1_GetLast(&l->clients_free), struct PasswordListenerClient, list_node); + LinkedList1_Remove(&l->clients_free, &client->list_node); + LinkedList1_Append(&l->clients_used, &client->list_node); + + // allocate sslsocket structure + if (!(client->sock = (sslsocket *)malloc(sizeof(*client->sock)))) { + BLog(BLOG_ERROR, "malloc failedt"); + goto fail0; + } + + // accept connection + if (!BConnection_Init(&client->sock->con, BConnection_source_listener(&l->listener, NULL), l->bsys, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + BLog(BLOG_INFO, "Connection accepted"); + + // init connection interfaces + BConnection_SendAsync_Init(&client->sock->con); + BConnection_RecvAsync_Init(&client->sock->con); + + StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->sock->con); + StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->sock->con); + + if (l->ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&client->sock->bottom_prfd, send_if, recv_if, l->twd, l->ssl_flags)) { + BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(client->sock->ssl_prfd = SSL_ImportFD(l->model_prfd, &client->sock->bottom_prfd))) { + ASSERT_FORCE(PR_Close(&client->sock->bottom_prfd) == PR_SUCCESS) + goto fail2; + } + + // set server mode + if (SSL_ResetHandshake(client->sock->ssl_prfd, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(client->sock->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + + // initialize SSLConnection + BSSLConnection_Init(&client->sslcon, client->sock->ssl_prfd, 0, BReactor_PendingGroup(l->bsys), client, (BSSLConnection_handler)client_sslcon_handler); + + send_if = BSSLConnection_GetSendIf(&client->sslcon); + recv_if = BSSLConnection_GetRecvIf(&client->sslcon); + } + + // init receiver + SingleStreamReceiver_Init(&client->receiver, (uint8_t *)&client->recv_buffer, sizeof(client->recv_buffer), recv_if, BReactor_PendingGroup(l->bsys), client, (SingleStreamReceiver_handler)client_receiver_handler); + + return; + + // cleanup on error +fail3: + if (l->ssl) { + ASSERT_FORCE(PR_Close(client->sock->ssl_prfd) == PR_SUCCESS) + } +fail2: + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + BConnection_Free(&client->sock->con); +fail1: + free(client->sock); +fail0: + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); +} + +void client_connection_handler (struct PasswordListenerClient *client, int event) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "connection closed"); + } else { + BLog(BLOG_INFO, "connection error"); + } + + remove_client(client); +} + +void client_sslcon_handler (struct PasswordListenerClient *client, int event) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + ASSERT(l->ssl) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + BLog(BLOG_INFO, "SSL error"); + + remove_client(client); +} + +void client_receiver_handler (struct PasswordListenerClient *client) +{ + PasswordListener *l = client->l; + DebugObject_Access(&l->d_obj); + + // check password + uint64_t received_pass = ltoh64(client->recv_buffer); + BAVLNode *pw_tree_node = BAVL_LookupExact(&l->passwords, &received_pass); + if (!pw_tree_node) { + BLog(BLOG_WARNING, "unknown password"); + remove_client(client); + return; + } + PasswordListener_pwentry *pw_entry = UPPER_OBJECT(pw_tree_node, PasswordListener_pwentry, tree_node); + + BLog(BLOG_INFO, "Password recognized"); + + // remove password entry + BAVL_Remove(&l->passwords, &pw_entry->tree_node); + + // stop using any buffers before they get freed + if (l->ssl) { + BSSLConnection_ReleaseBuffers(&client->sslcon); + } + + // free receiver + SingleStreamReceiver_Free(&client->receiver); + + if (l->ssl) { + // free SSL connection + BSSLConnection_Free(&client->sslcon); + } else { + // free connection interfaces + BConnection_RecvAsync_Free(&client->sock->con); + BConnection_SendAsync_Free(&client->sock->con); + } + + // remove connection handler + BConnection_SetHandlers(&client->sock->con, NULL, NULL); + + // move client entry to free list + LinkedList1_Remove(&l->clients_used, &client->list_node); + LinkedList1_Append(&l->clients_free, &client->list_node); + + // give the socket to the handler + pw_entry->handler_client(pw_entry->user, client->sock); + return; +} + +int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key) +{ + ASSERT(BConnection_AddressSupported(listen_addr)) + ASSERT(max_clients > 0) + ASSERT(ssl == 0 || ssl == 1) + + // init arguments + l->bsys = bsys; + l->twd = twd; + l->ssl = ssl; + l->ssl_flags = ssl_flags; + + // allocate client entries + if (!(l->clients_data = (struct PasswordListenerClient *)BAllocArray(max_clients, sizeof(struct PasswordListenerClient)))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + if (l->ssl) { + // initialize model SSL fd + DummyPRFileDesc_Create(&l->model_dprfd); + if (!(l->model_prfd = SSL_ImportFD(NULL, &l->model_dprfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&l->model_dprfd) == PR_SUCCESS) + goto fail1; + } + + // set server certificate + if (SSL_ConfigSecureServer(l->model_prfd, cert, key, NSS_FindCertKEAType(cert)) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail2; + } + } + + // initialize client entries + LinkedList1_Init(&l->clients_free); + LinkedList1_Init(&l->clients_used); + for (int i = 0; i < max_clients; i++) { + struct PasswordListenerClient *conn = &l->clients_data[i]; + conn->l = l; + LinkedList1_Append(&l->clients_free, &conn->list_node); + } + + // initialize passwords tree + BAVL_Init(&l->passwords, OFFSET_DIFF(PasswordListener_pwentry, password, tree_node), (BAVL_comparator)password_comparator, NULL); + + // initialize listener + if (!BListener_Init(&l->listener, listen_addr, l->bsys, l, (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail2; + } + + DebugObject_Init(&l->d_obj); + return 1; + + // cleanup +fail2: + if (l->ssl) { + ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS) + } +fail1: + BFree(l->clients_data); +fail0: + return 0; +} + +void PasswordListener_Free (PasswordListener *l) +{ + DebugObject_Free(&l->d_obj); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&l->clients_used)) { + struct PasswordListenerClient *client = UPPER_OBJECT(node, struct PasswordListenerClient, list_node); + remove_client(client); + } + + // free listener + BListener_Free(&l->listener); + + // free model SSL file descriptor + if (l->ssl) { + ASSERT_FORCE(PR_Close(l->model_prfd) == PR_SUCCESS) + } + + // free client entries + BFree(l->clients_data); +} + +uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user) +{ + DebugObject_Access(&l->d_obj); + + while (1) { + // generate password + BRandom_randomize((uint8_t *)&entry->password, sizeof(entry->password)); + + // try inserting + if (BAVL_Insert(&l->passwords, &entry->tree_node, NULL)) { + break; + } + } + + entry->handler_client = handler_client; + entry->user = user; + + return entry->password; +} + +void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry) +{ + DebugObject_Access(&l->d_obj); + + // remove + BAVL_Remove(&l->passwords, &entry->tree_node); +} diff --git a/external/badvpn_dns/client/PasswordListener.h b/external/badvpn_dns/client/PasswordListener.h new file mode 100644 index 00000000..bbc0bd11 --- /dev/null +++ b/external/badvpn_dns/client/PasswordListener.h @@ -0,0 +1,156 @@ +/** + * @file PasswordListener.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used to listen on a socket, accept clients and identify them + * based on a number they send. + */ + +#ifndef BADVPN_CLIENT_PASSWORDLISTENER_H +#define BADVPN_CLIENT_PASSWORDLISTENER_H + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler function called when a client identifies itself with a password + * belonging to one of the password entries. + * The password entry is unregistered before the handler is called + * and must not be unregistered again. + * + * @param user as in {@link PasswordListener_AddEntry} + * @param sock structure containing a {@link BConnection} and, if TLS is enabled, + * the SSL socket with the bottom layer connected to the async interfaces + * of the {@link BConnection} object. The structure was allocated with + * malloc() and the user is responsible for freeing it. + */ +typedef void (*PasswordListener_handler_client) (void *user, sslsocket *sock); + +struct PasswordListenerClient; + +/** + * Object used to listen on a socket, accept clients and identify them + * based on a number they send. + */ +typedef struct { + BReactor *bsys; + BThreadWorkDispatcher *twd; + int ssl; + int ssl_flags; + PRFileDesc model_dprfd; + PRFileDesc *model_prfd; + struct PasswordListenerClient *clients_data; + LinkedList1 clients_free; + LinkedList1 clients_used; + BAVL passwords; + BListener listener; + DebugObject d_obj; +} PasswordListener; + +typedef struct { + uint64_t password; + BAVLNode tree_node; + PasswordListener_handler_client handler_client; + void *user; +} PasswordListener_pwentry; + +struct PasswordListenerClient { + PasswordListener *l; + LinkedList1Node list_node; + sslsocket *sock; + BSSLConnection sslcon; + SingleStreamReceiver receiver; + uint64_t recv_buffer; +}; + +/** + * Initializes the object. + * + * @param l the object + * @param bsys reactor we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param listen_addr address to listen on. Must be supported according to {@link BConnection_AddressSupported}. + * @param max_clients maximum number of client to hold until they are identified. + * Must be >0. + * @param ssl whether to use TLS. Must be 1 or 0. + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param cert if using TLS, the server certificate + * @param key if using TLS, the private key + * @return 1 on success, 0 on failure + */ +int PasswordListener_Init (PasswordListener *l, BReactor *bsys, BThreadWorkDispatcher *twd, BAddr listen_addr, int max_clients, int ssl, int ssl_flags, CERTCertificate *cert, SECKEYPrivateKey *key) WARN_UNUSED; + +/** + * Frees the object. + * + * @param l the object + */ +void PasswordListener_Free (PasswordListener *l); + +/** + * Registers a password entry. + * + * @param l the object + * @param entry uninitialized entry structure + * @param handler_client handler function to call when a client identifies + * with the password which this function returns + * @param user value to pass to handler function + * @return password which a client should send to be recognized and + * dispatched to the handler function. Should be treated as a numeric + * value, which a client should as a little-endian 64-bit unsigned integer + * when it connects. + */ +uint64_t PasswordListener_AddEntry (PasswordListener *l, PasswordListener_pwentry *entry, PasswordListener_handler_client handler_client, void *user); + +/** + * Unregisters a password entry. + * Note that when a client is dispatched, its entry is unregistered + * automatically and must not be unregistered again here. + * + * @param l the object + * @param entry entry to unregister + */ +void PasswordListener_RemoveEntry (PasswordListener *l, PasswordListener_pwentry *entry); + +#endif diff --git a/external/badvpn_dns/client/PeerChat.c b/external/badvpn_dns/client/PeerChat.c new file mode 100644 index 00000000..d9dd9662 --- /dev/null +++ b/external/badvpn_dns/client/PeerChat.c @@ -0,0 +1,433 @@ +/** + * @file PeerChat.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#include "PeerChat.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void report_error (PeerChat *o) +{ + DebugError_AssertNoError(&o->d_err); + + DEBUGERROR(&o->d_err, o->handler_error(o->user)) + return; +} + +static void recv_job_handler (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv_data_len >= 0) + ASSERT(o->recv_data_len <= SC_MAX_MSGLEN) + + int data_len = o->recv_data_len; + + // set no received data + o->recv_data_len = -1; + +#ifdef PEERCHAT_SIMULATE_ERROR + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < PEERCHAT_SIMULATE_ERROR) { + PeerLog(o, BLOG_ERROR, "simulate error"); + report_error(o); + return; + } +#endif + + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + // buffer data + if (!SimpleStreamBuffer_Write(&o->ssl_recv_buf, o->recv_data, data_len)) { + PeerLog(o, BLOG_ERROR, "out of recv buffer"); + report_error(o); + return; + } + } else { + // call message handler + o->handler_message(o->user, o->recv_data, data_len); + return; + } +} + +static void ssl_con_handler (PeerChat *o, int event) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + PeerLog(o, BLOG_ERROR, "SSL error"); + + report_error(o); + return; +} + +static SECStatus client_auth_data_callback (PeerChat *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT) + + CERTCertificate *cert = CERT_DupCertificate(o->ssl_cert); + if (!cert) { + PeerLog(o, BLOG_ERROR, "CERT_DupCertificate failed"); + goto fail0; + } + + SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(o->ssl_key); + if (!key) { + PeerLog(o, BLOG_ERROR, "SECKEY_CopyPrivateKey failed"); + goto fail1; + } + + *pRetCert = cert; + *pRetKey = key; + return SECSuccess; + +fail1: + CERT_DestroyCertificate(cert); +fail0: + return SECFailure; +} + +static SECStatus auth_certificate_callback (PeerChat *o, PRFileDesc *fd, PRBool checkSig, PRBool isServer) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + + // This callback is used to bypass checking the server's domain name, as peers + // don't have domain names. We byte-compare the certificate to the one reported + // by the server anyway. + + SECStatus ret = SECFailure; + + CERTCertificate *cert = SSL_PeerCertificate(o->ssl_prfd); + if (!cert) { + PeerLog(o, BLOG_ERROR, "SSL_PeerCertificate failed"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail1; + } + + SECCertUsage cert_usage = (o->ssl_mode == PEERCHAT_SSL_CLIENT ? certUsageSSLServer : certUsageSSLClient); + + if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), cert, PR_TRUE, cert_usage, SSL_RevealPinArg(o->ssl_prfd)) != SECSuccess) { + goto fail2; + } + + // compare to certificate provided by the server + SECItem der = cert->derCert; + if (der.len != o->ssl_peer_cert_len || memcmp(der.data, o->ssl_peer_cert, der.len)) { + PeerLog(o, BLOG_ERROR, "peer certificate doesn't match"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail2; + } + + ret = SECSuccess; + +fail2: + CERT_DestroyCertificate(cert); +fail1: + return ret; +} + +static void ssl_recv_if_handler_send (PeerChat *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // accept packet + PacketPassInterface_Done(&o->ssl_recv_if); + + // call message handler + o->handler_message(o->user, data, data_len); + return; +} + +static void ssl_recv_decoder_handler_error (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->ssl_mode == PEERCHAT_SSL_CLIENT || o->ssl_mode == PEERCHAT_SSL_SERVER) + + PeerLog(o, BLOG_ERROR, "decoder error"); + + report_error(o); + return; +} + +int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key, + uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, + BLog_logfunc logfunc, + PeerChat_handler_error handler_error, + PeerChat_handler_message handler_message) +{ + ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_mode == PEERCHAT_SSL_CLIENT || ssl_mode == PEERCHAT_SSL_SERVER) + ASSERT(ssl_mode == PEERCHAT_SSL_NONE || ssl_peer_cert_len >= 0) + ASSERT(logfunc) + ASSERT(handler_error) + ASSERT(handler_message) + + // init arguments + o->ssl_mode = ssl_mode; + o->ssl_cert = ssl_cert; + o->ssl_key = ssl_key; + o->ssl_peer_cert = ssl_peer_cert; + o->ssl_peer_cert_len = ssl_peer_cert_len; + o->user = user; + o->logfunc = logfunc; + o->handler_error = handler_error; + o->handler_message = handler_message; + + // init copier + PacketCopier_Init(&o->copier, SC_MAX_MSGLEN, pg); + + // init SC encoder + SCOutmsgEncoder_Init(&o->sc_encoder, peer_id, PacketCopier_GetOutput(&o->copier), pg); + + // init PacketProto encoder + PacketProtoEncoder_Init(&o->pp_encoder, SCOutmsgEncoder_GetOutput(&o->sc_encoder), pg); + + // init recv job + BPending_Init(&o->recv_job, pg, (BPending_handler)recv_job_handler, o); + + // set no received data + o->recv_data_len = -1; + + PacketPassInterface *send_buf_output = PacketCopier_GetInput(&o->copier); + + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + // init receive buffer + if (!SimpleStreamBuffer_Init(&o->ssl_recv_buf, PEERCHAT_SSL_RECV_BUF_SIZE, pg)) { + PeerLog(o, BLOG_ERROR, "SimpleStreamBuffer_Init failed"); + goto fail1; + } + + // init SSL StreamPacketSender + StreamPacketSender_Init(&o->ssl_sp_sender, send_buf_output, pg); + + // init SSL bottom prfd + if (!BSSLConnection_MakeBackend(&o->ssl_bottom_prfd, StreamPacketSender_GetInput(&o->ssl_sp_sender), SimpleStreamBuffer_GetOutput(&o->ssl_recv_buf), twd, ssl_flags)) { + PeerLog(o, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // init SSL prfd + if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->ssl_bottom_prfd))) { + ASSERT_FORCE(PR_Close(&o->ssl_bottom_prfd) == PR_SUCCESS) + PeerLog(o, BLOG_ERROR, "SSL_ImportFD failed"); + goto fail2; + } + + // set client or server mode + if (SSL_ResetHandshake(o->ssl_prfd, (o->ssl_mode == PEERCHAT_SSL_SERVER ? PR_TRUE : PR_FALSE)) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + if (o->ssl_mode == PEERCHAT_SSL_SERVER) { + // set server certificate + if (SSL_ConfigSecureServer(o->ssl_prfd, o->ssl_cert, o->ssl_key, NSS_FindCertKEAType(o->ssl_cert)) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(o->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(o->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + } else { + // set client certificate callback + if (SSL_GetClientAuthDataHook(o->ssl_prfd, (SSLGetClientAuthData)client_auth_data_callback, o) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail3; + } + } + + // set verify peer certificate hook + if (SSL_AuthCertificateHook(o->ssl_prfd, (SSLAuthCertificate)auth_certificate_callback, o) != SECSuccess) { + PeerLog(o, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail3; + } + + // init SSL connection + BSSLConnection_Init(&o->ssl_con, o->ssl_prfd, 0, pg, o, (BSSLConnection_handler)ssl_con_handler); + + // init SSL PacketStreamSender + PacketStreamSender_Init(&o->ssl_ps_sender, BSSLConnection_GetSendIf(&o->ssl_con), sizeof(struct packetproto_header) + SC_MAX_MSGLEN, pg); + + // init SSL copier + PacketCopier_Init(&o->ssl_copier, SC_MAX_MSGLEN, pg); + + // init SSL encoder + PacketProtoEncoder_Init(&o->ssl_encoder, PacketCopier_GetOutput(&o->ssl_copier), pg); + + // init SSL buffer + if (!SinglePacketBuffer_Init(&o->ssl_buffer, PacketProtoEncoder_GetOutput(&o->ssl_encoder), PacketStreamSender_GetInput(&o->ssl_ps_sender), pg)) { + PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + // init receive interface + PacketPassInterface_Init(&o->ssl_recv_if, SC_MAX_MSGLEN, (PacketPassInterface_handler_send)ssl_recv_if_handler_send, o, pg); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->ssl_recv_decoder, BSSLConnection_GetRecvIf(&o->ssl_con), &o->ssl_recv_if, pg, o, (PacketProtoDecoder_handler_error)ssl_recv_decoder_handler_error)) { + PeerLog(o, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail5; + } + + send_buf_output = PacketCopier_GetInput(&o->ssl_copier); + } + + // init send writer + BufferWriter_Init(&o->send_writer, SC_MAX_MSGLEN, pg); + + // init send buffer + if (!PacketBuffer_Init(&o->send_buf, BufferWriter_GetOutput(&o->send_writer), send_buf_output, PEERCHAT_SEND_BUF_SIZE, pg)) { + PeerLog(o, BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail6; + } + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); + return 1; + +fail6: + BufferWriter_Free(&o->send_writer); + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + PacketProtoDecoder_Free(&o->ssl_recv_decoder); +fail5: + PacketPassInterface_Free(&o->ssl_recv_if); + SinglePacketBuffer_Free(&o->ssl_buffer); +fail4: + PacketProtoEncoder_Free(&o->ssl_encoder); + PacketCopier_Free(&o->ssl_copier); + PacketStreamSender_Free(&o->ssl_ps_sender); + BSSLConnection_Free(&o->ssl_con); +fail3: + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) +fail2: + StreamPacketSender_Free(&o->ssl_sp_sender); + SimpleStreamBuffer_Free(&o->ssl_recv_buf); + } +fail1: + BPending_Free(&o->recv_job); + PacketProtoEncoder_Free(&o->pp_encoder); + SCOutmsgEncoder_Free(&o->sc_encoder); + PacketCopier_Free(&o->copier); + return 0; +} + +void PeerChat_Free (PeerChat *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // stop using any buffers before they get freed + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + BSSLConnection_ReleaseBuffers(&o->ssl_con); + } + + PacketBuffer_Free(&o->send_buf); + BufferWriter_Free(&o->send_writer); + if (o->ssl_mode != PEERCHAT_SSL_NONE) { + PacketProtoDecoder_Free(&o->ssl_recv_decoder); + PacketPassInterface_Free(&o->ssl_recv_if); + SinglePacketBuffer_Free(&o->ssl_buffer); + PacketProtoEncoder_Free(&o->ssl_encoder); + PacketCopier_Free(&o->ssl_copier); + PacketStreamSender_Free(&o->ssl_ps_sender); + BSSLConnection_Free(&o->ssl_con); + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + StreamPacketSender_Free(&o->ssl_sp_sender); + SimpleStreamBuffer_Free(&o->ssl_recv_buf); + } + BPending_Free(&o->recv_job); + PacketProtoEncoder_Free(&o->pp_encoder); + SCOutmsgEncoder_Free(&o->sc_encoder); + PacketCopier_Free(&o->copier); +} + +PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o) +{ + DebugObject_Access(&o->d_obj); + + return PacketProtoEncoder_GetOutput(&o->pp_encoder); +} + +void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv_data_len == -1) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // remember data + o->recv_data = data; + o->recv_data_len = data_len; + + // set received job + BPending_Set(&o->recv_job); +} + +int PeerChat_StartMessage (PeerChat *o, uint8_t **data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + return BufferWriter_StartPacket(&o->send_writer, data); +} + +void PeerChat_EndMessage (PeerChat *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + BufferWriter_EndPacket(&o->send_writer, data_len); +} diff --git a/external/badvpn_dns/client/PeerChat.h b/external/badvpn_dns/client/PeerChat.h new file mode 100644 index 00000000..674e3745 --- /dev/null +++ b/external/badvpn_dns/client/PeerChat.h @@ -0,0 +1,123 @@ +/** + * @file PeerChat.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PEERCHAT_H +#define BADVPN_PEERCHAT_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEERCHAT_SSL_NONE 0 +#define PEERCHAT_SSL_CLIENT 1 +#define PEERCHAT_SSL_SERVER 2 + +#define PEERCHAT_SSL_RECV_BUF_SIZE 4096 +#define PEERCHAT_SEND_BUF_SIZE 200 + +//#define PEERCHAT_SIMULATE_ERROR 40 + +typedef void (*PeerChat_handler_error) (void *user); +typedef void (*PeerChat_handler_message) (void *user, uint8_t *data, int data_len); + +typedef struct { + int ssl_mode; + CERTCertificate *ssl_cert; + SECKEYPrivateKey *ssl_key; + uint8_t *ssl_peer_cert; + int ssl_peer_cert_len; + void *user; + BLog_logfunc logfunc; + PeerChat_handler_error handler_error; + PeerChat_handler_message handler_message; + + // transport + PacketProtoEncoder pp_encoder; + SCOutmsgEncoder sc_encoder; + PacketCopier copier; + BPending recv_job; + uint8_t *recv_data; + int recv_data_len; + + // SSL transport + StreamPacketSender ssl_sp_sender; + SimpleStreamBuffer ssl_recv_buf; + + // SSL connection + PRFileDesc ssl_bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection ssl_con; + + // SSL higher layer + PacketStreamSender ssl_ps_sender; + SinglePacketBuffer ssl_buffer; + PacketProtoEncoder ssl_encoder; + PacketCopier ssl_copier; + PacketProtoDecoder ssl_recv_decoder; + PacketPassInterface ssl_recv_if; + + // higher layer send buffer + PacketBuffer send_buf; + BufferWriter send_writer; + + DebugError d_err; + DebugObject d_obj; +} PeerChat; + +int PeerChat_Init (PeerChat *o, peerid_t peer_id, int ssl_mode, int ssl_flags, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key, + uint8_t *ssl_peer_cert, int ssl_peer_cert_len, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, + BLog_logfunc logfunc, + PeerChat_handler_error handler_error, + PeerChat_handler_message handler_message) WARN_UNUSED; +void PeerChat_Free (PeerChat *o); +PacketRecvInterface * PeerChat_GetSendOutput (PeerChat *o); +void PeerChat_InputReceived (PeerChat *o, uint8_t *data, int data_len); +int PeerChat_StartMessage (PeerChat *o, uint8_t **data) WARN_UNUSED; +void PeerChat_EndMessage (PeerChat *o, int data_len); + +#endif diff --git a/external/badvpn_dns/client/SCOutmsgEncoder.c b/external/badvpn_dns/client/SCOutmsgEncoder.c new file mode 100644 index 00000000..83e8b27c --- /dev/null +++ b/external/badvpn_dns/client/SCOutmsgEncoder.c @@ -0,0 +1,104 @@ +/** + * @file SCOutmsgEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "SCOutmsgEncoder.h" + +static void output_handler_recv (SCOutmsgEncoder *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->output_packet) + ASSERT(data) + + // schedule receive + o->output_packet = data; + PacketRecvInterface_Receiver_Recv(o->input, o->output_packet + SCOUTMSG_OVERHEAD); +} + +static void input_handler_done (SCOutmsgEncoder *o, int in_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_packet) + + // write SC header + struct sc_header header; + header.type = htol8(SCID_OUTMSG); + memcpy(o->output_packet, &header, sizeof(header)); + + // write outmsg + struct sc_client_outmsg outmsg; + outmsg.clientid = htol16(o->peer_id); + memcpy(o->output_packet + sizeof(header), &outmsg, sizeof(outmsg)); + + // finish output packet + o->output_packet = NULL; + PacketRecvInterface_Done(&o->output, SCOUTMSG_OVERHEAD + in_len); +} + +void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - SCOUTMSG_OVERHEAD) + + // init arguments + o->peer_id = peer_id; + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketRecvInterface_Init(&o->output, SCOUTMSG_OVERHEAD + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no output packet + o->output_packet = NULL; + + DebugObject_Init(&o->d_obj); +} + +void SCOutmsgEncoder_Free (SCOutmsgEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/client/SCOutmsgEncoder.h b/external/badvpn_dns/client/SCOutmsgEncoder.h new file mode 100644 index 00000000..05d4cb27 --- /dev/null +++ b/external/badvpn_dns/client/SCOutmsgEncoder.h @@ -0,0 +1,76 @@ +/** + * @file SCOutmsgEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SCOUTMSGENCODER_H +#define BADVPN_SCOUTMSGENCODER_H + +#include +#include +#include + +#define SCOUTMSG_OVERHEAD (sizeof(struct sc_header) + sizeof(struct sc_client_outmsg)) + +/** + * A {@link PacketRecvInterface} layer which encodes SCProto outgoing messages. + */ +typedef struct { + peerid_t peer_id; + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *output_packet; + DebugObject d_obj; +} SCOutmsgEncoder; + +/** + * Initializes the object. + * + * @param o the object + * @param peer_id destination peer for messages + * @param input input interface. Its MTU muse be <= (INT_MAX - SCOUTMSG_OVERHEAD). + * @param pg pending group we live in + */ +void SCOutmsgEncoder_Init (SCOutmsgEncoder *o, peerid_t peer_id, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SCOutmsgEncoder_Free (SCOutmsgEncoder *o); + +/** + * Returns the output interface. + * The MTU of the interface will be (SCOUTMSG_OVERHEAD + input MTU). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SCOutmsgEncoder_GetOutput (SCOutmsgEncoder *o); + +#endif diff --git a/external/badvpn_dns/client/SPProtoDecoder.c b/external/badvpn_dns/client/SPProtoDecoder.c new file mode 100644 index 00000000..0855162c --- /dev/null +++ b/external/badvpn_dns/client/SPProtoDecoder.c @@ -0,0 +1,398 @@ +/** + * @file SPProtoDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "SPProtoDecoder.h" + +#include + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void decode_work_func (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->in_len <= o->input_mtu) + + uint8_t *in = o->in; + int in_len = o->in_len; + + o->tw_out_len = -1; + + uint8_t *plaintext; + int plaintext_len; + + // decrypt if needed + if (!SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + plaintext = in; + plaintext_len = in_len; + } else { + // input must be a multiple of blocks size + if (in_len % o->enc_block_size != 0) { + PeerLog(o, BLOG_WARNING, "packet size not a multiple of block size"); + return; + } + + // input must have an IV block + if (in_len < o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet does not have an IV"); + return; + } + + // check if we have encryption key + if (!o->have_encryption_key) { + PeerLog(o, BLOG_WARNING, "have no encryption key"); + return; + } + + // copy IV as BEncryption_Decrypt changes the IV + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv, in, o->enc_block_size); + + // decrypt + uint8_t *ciphertext = in + o->enc_block_size; + int ciphertext_len = in_len - o->enc_block_size; + plaintext = o->buf; + BEncryption_Decrypt(&o->encryptor, ciphertext, plaintext, ciphertext_len, iv); + + // read padding + if (ciphertext_len < o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet does not have a padding block"); + return; + } + int i; + for (i = ciphertext_len - 1; i >= ciphertext_len - o->enc_block_size; i--) { + if (plaintext[i] == 1) { + break; + } + if (plaintext[i] != 0) { + PeerLog(o, BLOG_WARNING, "packet padding wrong (nonzero byte)"); + return; + } + } + if (i < ciphertext_len - o->enc_block_size) { + PeerLog(o, BLOG_WARNING, "packet padding wrong (all zeroes)"); + return; + } + plaintext_len = i; + } + + // check for header + if (plaintext_len < SPPROTO_HEADER_LEN(o->sp_params)) { + PeerLog(o, BLOG_WARNING, "packet has no header"); + return; + } + uint8_t *header = plaintext; + + // check data length + if (plaintext_len - SPPROTO_HEADER_LEN(o->sp_params) > o->output_mtu) { + PeerLog(o, BLOG_WARNING, "packet too long"); + return; + } + + // check OTP + if (SPPROTO_HAVE_OTP(o->sp_params)) { + // remember seed and OTP (can't check from here) + struct spproto_otpdata header_otpd; + memcpy(&header_otpd, header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), sizeof(header_otpd)); + o->tw_out_seed_id = ltoh16(header_otpd.seed_id); + o->tw_out_otp = header_otpd.otp; + } + + // check hash + if (SPPROTO_HAVE_HASH(o->sp_params)) { + uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params); + // read hash + uint8_t hash[BHASH_MAX_SIZE]; + memcpy(hash, header_hash, o->hash_size); + // zero hash in packet + memset(header_hash, 0, o->hash_size); + // calculate hash + uint8_t hash_calc[BHASH_MAX_SIZE]; + BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash_calc); + // set hash field to its original value + memcpy(header_hash, hash, o->hash_size); + // compare hashes + if (memcmp(hash, hash_calc, o->hash_size)) { + PeerLog(o, BLOG_WARNING, "packet has wrong hash"); + return; + } + } + + // return packet + o->tw_out = plaintext + SPPROTO_HEADER_LEN(o->sp_params); + o->tw_out_len = plaintext_len - SPPROTO_HEADER_LEN(o->sp_params); +} + +static void decode_work_handler (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->tw_have) + DebugObject_Access(&o->d_obj); + + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // check OTP + if (SPPROTO_HAVE_OTP(o->sp_params) && o->tw_out_len >= 0) { + if (!OTPChecker_CheckOTP(&o->otpchecker, o->tw_out_seed_id, o->tw_out_otp)) { + PeerLog(o, BLOG_WARNING, "packet has wrong OTP"); + o->tw_out_len = -1; + } + } + + if (o->tw_out_len < 0) { + // cannot decode, finish input packet + PacketPassInterface_Done(&o->input); + o->in_len = -1; + } else { + // submit decoded packet to output + PacketPassInterface_Sender_Send(o->output, o->tw_out, o->tw_out_len); + } +} + +static void input_handler_send (SPProtoDecoder *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember input + o->in = data; + o->in_len = data_len; + + // start decoding + BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)decode_work_handler, o, (BThreadWork_work_func)decode_work_func, o); + o->tw_have = 1; +} + +static void output_handler_done (SPProtoDecoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // finish input packet + PacketPassInterface_Done(&o->input); + o->in_len = -1; +} + +static void maybe_stop_work_and_ignore (SPProtoDecoder *o) +{ + ASSERT(!(o->tw_have) || o->in_len >= 0) + + if (o->tw_have) { + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // ignore packet, receive next one + PacketPassInterface_Done(&o->input); + o->in_len = -1; + } +} + +int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc) +{ + spproto_assert_security_params(sp_params); + ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketPassInterface_GetMTU(output)) >= 0) + ASSERT(!SPPROTO_HAVE_OTP(sp_params) || num_otp_seeds >= 2) + + // init arguments + o->output = output; + o->sp_params = sp_params; + o->twd = twd; + o->user = user; + o->logfunc = logfunc; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // remember output MTU + o->output_mtu = PacketPassInterface_GetMTU(o->output); + + // calculate hash size + if (SPPROTO_HAVE_HASH(o->sp_params)) { + o->hash_size = BHash_size(o->sp_params.hash_mode); + } + + // calculate encryption block and key sizes + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode); + o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode); + } + + // calculate input MTU + o->input_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->output_mtu); + + // allocate plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->output_mtu + 1), o->enc_block_size); + if (!(o->buf = (uint8_t *)malloc(buf_size))) { + goto fail0; + } + } + + // init input + PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // init OTP checker + if (SPPROTO_HAVE_OTP(o->sp_params)) { + if (!OTPChecker_Init(&o->otpchecker, o->sp_params.otp_num, o->sp_params.otp_mode, num_otp_seeds, o->twd)) { + goto fail1; + } + } + + // have no encryption key + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->have_encryption_key = 0; + } + + // have no input packet + o->in_len = -1; + + // have no work + o->tw_have = 0; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketPassInterface_Free(&o->input); + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } +fail0: + return 0; +} + +void SPProtoDecoder_Free (SPProtoDecoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + } + + // free encryptor + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // free OTP checker + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPChecker_Free(&o->otpchecker); + } + + // free input + PacketPassInterface_Free(&o->input); + + // free plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } +} + +PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work_and_ignore(o); + + // free encryptor + if (o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // init encryptor + BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_DECRYPT, o->sp_params.encryption_mode, encryption_key); + + // have encryption key + o->have_encryption_key = 1; +} + +void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work_and_ignore(o); + + if (o->have_encryption_key) { + // free encryptor + BEncryption_Free(&o->encryptor); + + // have no encryption key + o->have_encryption_key = 0; + } +} + +void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + OTPChecker_AddSeed(&o->otpchecker, seed_id, key, iv); +} + +void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + OTPChecker_RemoveSeeds(&o->otpchecker); +} + +void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPChecker_SetHandlers(&o->otpchecker, otp_handler, user); + } +} diff --git a/external/badvpn_dns/client/SPProtoDecoder.h b/external/badvpn_dns/client/SPProtoDecoder.h new file mode 100644 index 00000000..3b5de713 --- /dev/null +++ b/external/badvpn_dns/client/SPProtoDecoder.h @@ -0,0 +1,171 @@ +/** + * @file SPProtoDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes packets according to SPProto. + */ + +#ifndef BADVPN_CLIENT_SPPROTODECODER_H +#define BADVPN_CLIENT_SPPROTODECODER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler called when OTP generation for a new seed is finished. + * + * @param user as in {@link SPProtoDecoder_Init} + */ +typedef void (*SPProtoDecoder_otp_handler) (void *user); + +/** + * Object which decodes packets according to SPProto. + * Input is with {@link PacketPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + PacketPassInterface *output; + struct spproto_security_params sp_params; + BThreadWorkDispatcher *twd; + void *user; + BLog_logfunc logfunc; + int output_mtu; + int hash_size; + int enc_block_size; + int enc_key_size; + int input_mtu; + uint8_t *buf; + PacketPassInterface input; + OTPChecker otpchecker; + int have_encryption_key; + BEncryption encryptor; + uint8_t *in; + int in_len; + int tw_have; + BThreadWork tw; + uint16_t tw_out_seed_id; + otp_t tw_out_otp; + uint8_t *tw_out; + int tw_out_len; + DebugObject d_obj; +} SPProtoDecoder; + +/** + * Initializes the object. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param output output interface. Its MTU must not be too large, i.e. this must hold: + * spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU) >= 0 + * @param sp_params SPProto parameters + * @param encryption_key if using encryption, the encryption key + * @param num_otp_seeds if using OTPs, how many OTP seeds to keep for checking + * receiving packets. Must be >=2 if using OTPs. + * @param pg pending group + * @param twd thread work dispatcher + * @param user argument to handlers + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @return 1 on success, 0 on failure + */ +int SPProtoDecoder_Init (SPProtoDecoder *o, PacketPassInterface *output, struct spproto_security_params sp_params, int num_otp_seeds, BPendingGroup *pg, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void SPProtoDecoder_Free (SPProtoDecoder *o); + +/** + * Returns the input interface. + * The MTU of the input interface will depend on the output MTU and security parameters, + * that is spproto_carrier_mtu_for_payload_mtu(sp_params, output MTU). + * + * @param o the object + * @return input interface + */ +PacketPassInterface * SPProtoDecoder_GetInput (SPProtoDecoder *o); + +/** + * Sets an encryption key for decrypting packets. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void SPProtoDecoder_SetEncryptionKey (SPProtoDecoder *o, uint8_t *encryption_key); + +/** + * Removes an encryption key if one is configured. + * Encryption must be enabled. + * + * @param o the object + */ +void SPProtoDecoder_RemoveEncryptionKey (SPProtoDecoder *o); + +/** + * Starts generating OTPs for a seed to check received packets against. + * OTPs for this seed will not be recognized until the {@link SPProtoDecoder_otp_handler} handler + * is called. + * If OTPs are still being generated for the previous seed, it will be forgotten. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void SPProtoDecoder_AddOTPSeed (SPProtoDecoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all OTP seeds for checking received packets against. + * OTPs must be enabled. + * + * @param o the object + */ +void SPProtoDecoder_RemoveOTPSeeds (SPProtoDecoder *o); + +/** + * Sets handlers. + * + * @param o the object + * @param otp_handler handler called when OTP generation is finished + * @param user argument to handler + */ +void SPProtoDecoder_SetHandlers (SPProtoDecoder *o, SPProtoDecoder_otp_handler otp_handler, void *user); + +#endif diff --git a/external/badvpn_dns/client/SPProtoEncoder.c b/external/badvpn_dns/client/SPProtoEncoder.c new file mode 100644 index 00000000..fbbab50a --- /dev/null +++ b/external/badvpn_dns/client/SPProtoEncoder.c @@ -0,0 +1,436 @@ +/** + * @file SPProtoEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "SPProtoEncoder.h" + +static int can_encode (SPProtoEncoder *o); +static void encode_packet (SPProtoEncoder *o); +static void encode_work_func (SPProtoEncoder *o); +static void encode_work_handler (SPProtoEncoder *o); +static void maybe_encode (SPProtoEncoder *o); +static void output_handler_recv (SPProtoEncoder *o, uint8_t *data); +static void input_handler_done (SPProtoEncoder *o, int data_len); +static void handler_job_hander (SPProtoEncoder *o); +static void otpgenerator_handler (SPProtoEncoder *o); +static void maybe_stop_work (SPProtoEncoder *o); + +static int can_encode (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + + return ( + (!SPPROTO_HAVE_OTP(o->sp_params) || OTPGenerator_GetPosition(&o->otpgen) < o->sp_params.otp_num) && + (!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key) + ); +} + +static void encode_packet (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + ASSERT(can_encode(o)) + + // generate OTP, remember seed ID + if (SPPROTO_HAVE_OTP(o->sp_params)) { + o->tw_seed_id = o->otpgen_seed_id; + o->tw_otp = OTPGenerator_GetOTP(&o->otpgen); + } + + // start work + BThreadWork_Init(&o->tw, o->twd, (BThreadWork_handler_done)encode_work_handler, o, (BThreadWork_work_func)encode_work_func, o); + o->tw_have = 1; + + // schedule OTP warning handler + if (SPPROTO_HAVE_OTP(o->sp_params) && OTPGenerator_GetPosition(&o->otpgen) == o->otp_warning_count) { + BPending_Set(&o->handler_job); + } +} + +static void encode_work_func (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(!SPPROTO_HAVE_ENCRYPTION(o->sp_params) || o->have_encryption_key) + + ASSERT(o->in_len <= o->input_mtu) + + // determine plaintext location + uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out); + + // plaintext begins with header + uint8_t *header = plaintext; + + // plaintext is header + payload + int plaintext_len = SPPROTO_HEADER_LEN(o->sp_params) + o->in_len; + + // write OTP + if (SPPROTO_HAVE_OTP(o->sp_params)) { + struct spproto_otpdata header_otpd; + header_otpd.seed_id = htol16(o->tw_seed_id); + header_otpd.otp = o->tw_otp; + memcpy(header + SPPROTO_HEADER_OTPDATA_OFF(o->sp_params), &header_otpd, sizeof(header_otpd)); + } + + // write hash + if (SPPROTO_HAVE_HASH(o->sp_params)) { + uint8_t *header_hash = header + SPPROTO_HEADER_HASH_OFF(o->sp_params); + // zero hash field + memset(header_hash, 0, o->hash_size); + // calculate hash + uint8_t hash[BHASH_MAX_SIZE]; + BHash_calculate(o->sp_params.hash_mode, plaintext, plaintext_len, hash); + // set hash field + memcpy(header_hash, hash, o->hash_size); + } + + int out_len; + + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + // encrypting pad(header + payload) + int cyphertext_len = balign_up((plaintext_len + 1), o->enc_block_size); + + // write padding + plaintext[plaintext_len] = 1; + for (int i = plaintext_len + 1; i < cyphertext_len; i++) { + plaintext[i] = 0; + } + + // generate IV + BRandom_randomize(o->out, o->enc_block_size); + + // copy IV because BEncryption_Encrypt changes the IV + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv, o->out, o->enc_block_size); + + // encrypt + BEncryption_Encrypt(&o->encryptor, plaintext, o->out + o->enc_block_size, cyphertext_len, iv); + out_len = o->enc_block_size + cyphertext_len; + } else { + out_len = plaintext_len; + } + + // remember length + o->tw_out_len = out_len; +} + +static void encode_work_handler (SPProtoEncoder *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->out_have) + ASSERT(o->tw_have) + + // free work + BThreadWork_Free(&o->tw); + o->tw_have = 0; + + // finish packet + o->in_len = -1; + o->out_have = 0; + PacketRecvInterface_Done(&o->output, o->tw_out_len); +} + +static void maybe_encode (SPProtoEncoder *o) +{ + if (o->in_len >= 0 && o->out_have && !o->tw_have && can_encode(o)) { + encode_packet(o); + } +} + +static void output_handler_recv (SPProtoEncoder *o, uint8_t *data) +{ + ASSERT(o->in_len == -1) + ASSERT(!o->out_have) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_have = 1; + o->out = data; + + // determine plaintext location + uint8_t *plaintext = (SPPROTO_HAVE_ENCRYPTION(o->sp_params) ? o->buf : o->out); + + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, plaintext + SPPROTO_HEADER_LEN(o->sp_params)); +} + +static void input_handler_done (SPProtoEncoder *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + ASSERT(o->out_have) + ASSERT(!o->tw_have) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + + // encode if possible + if (can_encode(o)) { + encode_packet(o); + } +} + +static void handler_job_hander (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + if (o->handler) { + o->handler(o->user); + return; + } +} + +static void otpgenerator_handler (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // remember seed ID + o->otpgen_seed_id = o->otpgen_pending_seed_id; + + // possibly continue I/O + maybe_encode(o); +} + +static void maybe_stop_work (SPProtoEncoder *o) +{ + // stop existing work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + o->tw_have = 0; + } +} + +int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd) +{ + spproto_assert_security_params(sp_params); + ASSERT(spproto_carrier_mtu_for_payload_mtu(sp_params, PacketRecvInterface_GetMTU(input)) >= 0) + if (SPPROTO_HAVE_OTP(sp_params)) { + ASSERT(otp_warning_count > 0) + ASSERT(otp_warning_count <= sp_params.otp_num) + } + + // init arguments + o->input = input; + o->sp_params = sp_params; + o->otp_warning_count = otp_warning_count; + o->twd = twd; + + // set no handlers + o->handler = NULL; + + // calculate hash size + if (SPPROTO_HAVE_HASH(o->sp_params)) { + o->hash_size = BHash_size(o->sp_params.hash_mode); + } + + // calculate encryption block and key sizes + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->enc_block_size = BEncryption_cipher_block_size(o->sp_params.encryption_mode); + o->enc_key_size = BEncryption_cipher_key_size(o->sp_params.encryption_mode); + } + + // init otp generator + if (SPPROTO_HAVE_OTP(o->sp_params)) { + if (!OTPGenerator_Init(&o->otpgen, o->sp_params.otp_num, o->sp_params.otp_mode, o->twd, (OTPGenerator_handler)otpgenerator_handler, o)) { + goto fail0; + } + } + + // have no encryption key + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + o->have_encryption_key = 0; + } + + // remember input MTU + o->input_mtu = PacketRecvInterface_GetMTU(o->input); + + // calculate output MTU + o->output_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->input_mtu); + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // have no input in buffer + o->in_len = -1; + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output available + o->out_have = 0; + + // allocate plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + int buf_size = balign_up((SPPROTO_HEADER_LEN(o->sp_params) + o->input_mtu + 1), o->enc_block_size); + if (!(o->buf = (uint8_t *)malloc(buf_size))) { + goto fail1; + } + } + + // init handler job + BPending_Init(&o->handler_job, pg, (BPending_handler)handler_job_hander, o); + + // have no work + o->tw_have = 0; + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketRecvInterface_Free(&o->output); + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPGenerator_Free(&o->otpgen); + } +fail0: + return 0; +} + +void SPProtoEncoder_Free (SPProtoEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free work + if (o->tw_have) { + BThreadWork_Free(&o->tw); + } + + // free handler job + BPending_Free(&o->handler_job); + + // free plaintext buffer + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params)) { + free(o->buf); + } + + // free output + PacketRecvInterface_Free(&o->output); + + // free encryptor + if (SPPROTO_HAVE_ENCRYPTION(o->sp_params) && o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // free otp generator + if (SPPROTO_HAVE_OTP(o->sp_params)) { + OTPGenerator_Free(&o->otpgen); + } +} + +PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work(o); + + // free encryptor + if (o->have_encryption_key) { + BEncryption_Free(&o->encryptor); + } + + // init encryptor + BEncryption_Init(&o->encryptor, BENCRYPTION_MODE_ENCRYPT, o->sp_params.encryption_mode, encryption_key); + + // have encryption key + o->have_encryption_key = 1; + + // possibly continue I/O + maybe_encode(o); +} + +void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // stop existing work + maybe_stop_work(o); + + if (o->have_encryption_key) { + // free encryptor + BEncryption_Free(&o->encryptor); + + // have no encryption key + o->have_encryption_key = 0; + } +} + +void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // give seed to OTP generator + OTPGenerator_SetSeed(&o->otpgen, key, iv); + + // remember seed ID + o->otpgen_pending_seed_id = seed_id; +} + +void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o) +{ + ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) + DebugObject_Access(&o->d_obj); + + // reset OTP generator + OTPGenerator_Reset(&o->otpgen); +} + +void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->user = user; +} diff --git a/external/badvpn_dns/client/SPProtoEncoder.h b/external/badvpn_dns/client/SPProtoEncoder.h new file mode 100644 index 00000000..874f391a --- /dev/null +++ b/external/badvpn_dns/client/SPProtoEncoder.h @@ -0,0 +1,172 @@ +/** + * @file SPProtoEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets according to SPProto. + */ + +#ifndef BADVPN_CLIENT_SPPROTOENCODER_H +#define BADVPN_CLIENT_SPPROTOENCODER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Event context handler called when the remaining number of + * OTPs equals the warning number after having encoded a packet. + * + * @param user as in {@link SPProtoEncoder_Init} + */ +typedef void (*SPProtoEncoder_handler) (void *user); + +/** + * Object which encodes packets according to SPProto. + * + * Input is with {@link PacketRecvInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + PacketRecvInterface *input; + struct spproto_security_params sp_params; + int otp_warning_count; + SPProtoEncoder_handler handler; + BThreadWorkDispatcher *twd; + void *user; + int hash_size; + int enc_block_size; + int enc_key_size; + OTPGenerator otpgen; + uint16_t otpgen_seed_id; + uint16_t otpgen_pending_seed_id; + int have_encryption_key; + BEncryption encryptor; + int input_mtu; + int output_mtu; + int in_len; + PacketRecvInterface output; + int out_have; + uint8_t *out; + uint8_t *buf; + BPending handler_job; + int tw_have; + BThreadWork tw; + uint16_t tw_seed_id; + otp_t tw_otp; + int tw_out_len; + DebugObject d_obj; +} SPProtoEncoder; + +/** + * Initializes the object. + * The object is initialized in blocked state. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param o the object + * @param input input interface. Its MTU must not be too large, i.e. this must hold: + * spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU) >= 0 + * @param sp_params SPProto security parameters + * @param otp_warning_count If using OTPs, after how many encoded packets to call the handler. + * In this case, must be >0 and <=sp_params.otp_num. + * @param pg pending group + * @param twd thread work dispatcher + * @return 1 on success, 0 on failure + */ +int SPProtoEncoder_Init (SPProtoEncoder *o, PacketRecvInterface *input, struct spproto_security_params sp_params, int otp_warning_count, BPendingGroup *pg, BThreadWorkDispatcher *twd) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void SPProtoEncoder_Free (SPProtoEncoder *o); + +/** + * Returns the output interface. + * The MTU of the output interface will depend on the input MTU and security parameters, + * that is spproto_carrier_mtu_for_payload_mtu(sp_params, input MTU). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SPProtoEncoder_GetOutput (SPProtoEncoder *o); + +/** + * Sets an encryption key to use. + * Encryption must be enabled. + * + * @param o the object + * @param encryption_key key to use + */ +void SPProtoEncoder_SetEncryptionKey (SPProtoEncoder *o, uint8_t *encryption_key); + +/** + * Removes an encryption key if one is configured. + * Encryption must be enabled. + * + * @param o the object + */ +void SPProtoEncoder_RemoveEncryptionKey (SPProtoEncoder *o); + +/** + * Sets an OTP seed to use. + * OTPs must be enabled. + * + * @param o the object + * @param seed_id seed identifier + * @param key OTP encryption key + * @param iv OTP initialization vector + */ +void SPProtoEncoder_SetOTPSeed (SPProtoEncoder *o, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes the OTP seed if one is configured. + * OTPs must be enabled. + * + * @param o the object + */ +void SPProtoEncoder_RemoveOTPSeed (SPProtoEncoder *o); + +/** + * Sets handlers. + * + * @param o the object + * @param handler OTP warning handler + * @param user value to pass to handler + */ +void SPProtoEncoder_SetHandlers (SPProtoEncoder *o, SPProtoEncoder_handler handler, void *user); + +#endif diff --git a/external/badvpn_dns/client/SimpleStreamBuffer.c b/external/badvpn_dns/client/SimpleStreamBuffer.c new file mode 100644 index 00000000..74448cb8 --- /dev/null +++ b/external/badvpn_dns/client/SimpleStreamBuffer.c @@ -0,0 +1,144 @@ +/** + * @file SimpleStreamBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include "SimpleStreamBuffer.h" + +static void try_output (SimpleStreamBuffer *o) +{ + ASSERT(o->output_data_len > 0) + + // calculate number of bytes to output + int bytes = bmin_int(o->output_data_len, o->buf_used); + if (bytes == 0) { + return; + } + + // copy bytes to output + memcpy(o->output_data, o->buf, bytes); + + // shift buffer + memmove(o->buf, o->buf + bytes, o->buf_used - bytes); + o->buf_used -= bytes; + + // forget data + o->output_data_len = -1; + + // done + StreamRecvInterface_Done(&o->output, bytes); +} + +static void output_handler_recv (SimpleStreamBuffer *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_data_len == -1) + ASSERT(data) + ASSERT(data_len > 0) + + // remember data + o->output_data = data; + o->output_data_len = data_len; + + try_output(o); +} + +int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg) +{ + ASSERT(buf_size > 0) + + // init arguments + o->buf_size = buf_size; + + // init output + StreamRecvInterface_Init(&o->output, (StreamRecvInterface_handler_recv)output_handler_recv, o, pg); + + // allocate buffer + if (!(o->buf = (uint8_t *)BAlloc(buf_size))) { + goto fail1; + } + + // init buffer state + o->buf_used = 0; + + // set no output data + o->output_data_len = -1; + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + StreamRecvInterface_Free(&o->output); + return 0; +} + +void SimpleStreamBuffer_Free (SimpleStreamBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + BFree(o->buf); + + // free output + StreamRecvInterface_Free(&o->output); +} + +StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= 0) + + if (data_len > o->buf_size - o->buf_used) { + return 0; + } + + // copy to buffer + memcpy(o->buf + o->buf_used, data, data_len); + + // update buffer state + o->buf_used += data_len; + + // continue outputting + if (o->output_data_len > 0) { + try_output(o); + } + + return 1; +} diff --git a/external/badvpn_dns/client/SimpleStreamBuffer.h b/external/badvpn_dns/client/SimpleStreamBuffer.h new file mode 100644 index 00000000..31a55f73 --- /dev/null +++ b/external/badvpn_dns/client/SimpleStreamBuffer.h @@ -0,0 +1,52 @@ +/** + * @file SimpleStreamBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SIMPLESTREAMBUFFER_H +#define BADVPN_SIMPLESTREAMBUFFER_H + +#include +#include +#include + +typedef struct { + int buf_size; + StreamRecvInterface output; + uint8_t *buf; + int buf_used; + uint8_t *output_data; + int output_data_len; + DebugObject d_obj; +} SimpleStreamBuffer; + +int SimpleStreamBuffer_Init (SimpleStreamBuffer *o, int buf_size, BPendingGroup *pg) WARN_UNUSED; +void SimpleStreamBuffer_Free (SimpleStreamBuffer *o); +StreamRecvInterface * SimpleStreamBuffer_GetOutput (SimpleStreamBuffer *o); +int SimpleStreamBuffer_Write (SimpleStreamBuffer *o, uint8_t *data, int data_len) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/client/SinglePacketSource.c b/external/badvpn_dns/client/SinglePacketSource.c new file mode 100644 index 00000000..1c6a573a --- /dev/null +++ b/external/badvpn_dns/client/SinglePacketSource.c @@ -0,0 +1,85 @@ +/** + * @file SinglePacketSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "SinglePacketSource.h" + +static void output_handler_recv (SinglePacketSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + // if we already sent one packet, stop + if (o->sent) { + return; + } + + // set sent + o->sent = 1; + + // write packet + memcpy(data, o->packet, o->packet_len); + + // done + PacketRecvInterface_Done(&o->output, o->packet_len); +} + +void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg) +{ + ASSERT(packet_len >= 0) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + + // set not sent + o->sent = 0; + + // init output + PacketRecvInterface_Init(&o->output, o->packet_len, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void SinglePacketSource_Free (SinglePacketSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/client/SinglePacketSource.h b/external/badvpn_dns/client/SinglePacketSource.h new file mode 100644 index 00000000..85ca4266 --- /dev/null +++ b/external/badvpn_dns/client/SinglePacketSource.h @@ -0,0 +1,73 @@ +/** + * @file SinglePacketSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLEPACKETSOURCE_H +#define BADVPN_SINGLEPACKETSOURCE_H + +#include +#include + +/** + * An object which provides a single packet through {@link PacketRecvInterface}. + */ +typedef struct { + uint8_t *packet; + int packet_len; + int sent; + PacketRecvInterface output; + DebugObject d_obj; +} SinglePacketSource; + +/** + * Initializes the object. + * + * @param o the object + * @param packet packet to provide to the output. Must stay available until the packet is provided. + * @param packet_len length of packet. Must be >=0. + * @param pg pending group we live in + */ +void SinglePacketSource_Init (SinglePacketSource *o, uint8_t *packet, int packet_len, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SinglePacketSource_Free (SinglePacketSource *o); + +/** + * Returns the output interface. + * The MTU of the interface will be packet_len. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SinglePacketSource_GetOutput (SinglePacketSource *o); + +#endif diff --git a/external/badvpn_dns/client/StreamPeerIO.c b/external/badvpn_dns/client/StreamPeerIO.c new file mode 100644 index 00000000..3113c3b0 --- /dev/null +++ b/external/badvpn_dns/client/StreamPeerIO.c @@ -0,0 +1,712 @@ +/** + * @file StreamPeerIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#include + +#include + +#define MODE_NONE 0 +#define MODE_CONNECT 1 +#define MODE_LISTEN 2 + +#define CONNECT_STATE_CONNECTING 0 +#define CONNECT_STATE_HANDSHAKE 1 +#define CONNECT_STATE_SENDING 2 +#define CONNECT_STATE_SENT 3 +#define CONNECT_STATE_FINISHED 4 + +#define LISTEN_STATE_LISTENER 0 +#define LISTEN_STATE_GOTCLIENT 1 +#define LISTEN_STATE_FINISHED 2 + +#define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void decoder_handler_error (StreamPeerIO *pio); +static void connector_handler (StreamPeerIO *pio, int is_error); +static void connection_handler (StreamPeerIO *pio, int event); +static void connect_sslcon_handler (StreamPeerIO *pio, int event); +static void pwsender_handler (StreamPeerIO *pio); +static void listener_handler_client (StreamPeerIO *pio, sslsocket *sock); +static int init_io (StreamPeerIO *pio, sslsocket *sock); +static void free_io (StreamPeerIO *pio); +static void sslcon_handler (StreamPeerIO *pio, int event); +static SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer); +static SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey); +static int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert); +static void reset_state (StreamPeerIO *pio); +static void reset_and_report_error (StreamPeerIO *pio); + +void decoder_handler_error (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + + PeerLog(pio, BLOG_ERROR, "decoder error"); + + reset_and_report_error(pio); + return; +} + +void connector_handler (StreamPeerIO *pio, int is_error) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_CONNECTING) + + // check connection result + if (is_error) { + PeerLog(pio, BLOG_NOTICE, "connection failed"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&pio->connect.sock.con, BConnection_source_connector(&pio->connect.connector), pio->reactor, pio, (BConnection_handler)connection_handler)) { + PeerLog(pio, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + if (pio->ssl) { + // init connection interfaces + BConnection_SendAsync_Init(&pio->connect.sock.con); + BConnection_RecvAsync_Init(&pio->connect.sock.con); + + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&pio->connect.sock.bottom_prfd, BConnection_SendAsync_GetIf(&pio->connect.sock.con), BConnection_RecvAsync_GetIf(&pio->connect.sock.con), pio->twd, pio->ssl_flags)) { + PeerLog(pio, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail1; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(pio->connect.sock.ssl_prfd = SSL_ImportFD(NULL, &pio->connect.sock.bottom_prfd))) { + ASSERT_FORCE(PR_Close(&pio->connect.sock.bottom_prfd) == PR_SUCCESS) + goto fail1; + } + + // set client mode + if (SSL_ResetHandshake(pio->connect.sock.ssl_prfd, PR_FALSE) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail2; + } + + // set verify peer certificate hook + if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, (SSLAuthCertificate)client_auth_certificate_callback, pio) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail2; + } + + // set client certificate callback + if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, (SSLGetClientAuthData)client_client_auth_data_callback, pio) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail2; + } + + // init BSSLConnection + BSSLConnection_Init(&pio->connect.sslcon, pio->connect.sock.ssl_prfd, 1, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)connect_sslcon_handler); + + // change state + pio->connect.state = CONNECT_STATE_HANDSHAKE; + } else { + // init connection send interface + BConnection_SendAsync_Init(&pio->connect.sock.con); + + // init password sender + SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BConnection_SendAsync_GetIf(&pio->connect.sock.con), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler); + + // change state + pio->connect.state = CONNECT_STATE_SENDING; + } + + return; + + if (pio->ssl) { +fail2: + ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS) +fail1: + BConnection_RecvAsync_Free(&pio->connect.sock.con); + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + BConnection_Free(&pio->connect.sock.con); +fail0: + reset_and_report_error(pio); + return; +} + +void connection_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN) + ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state >= CONNECT_STATE_HANDSHAKE) + ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state >= LISTEN_STATE_FINISHED) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + PeerLog(pio, BLOG_NOTICE, "connection closed"); + } else { + PeerLog(pio, BLOG_NOTICE, "connection error"); + } + + reset_and_report_error(pio); + return; +} + +void connect_sslcon_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) + ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR) + + if (event == BSSLCONNECTION_EVENT_ERROR) { + PeerLog(pio, BLOG_NOTICE, "SSL error"); + + reset_and_report_error(pio); + return; + } + + // handshake complete + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + + // remove client certificate callback + if (SSL_GetClientAuthDataHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail0; + } + + // remove verify peer certificate callback + if (SSL_AuthCertificateHook(pio->connect.sock.ssl_prfd, NULL, NULL) != SECSuccess) { + PeerLog(pio, BLOG_ERROR, "SSL_AuthCertificateHook failed"); + goto fail0; + } + + // init password sender + SingleStreamSender_Init(&pio->connect.pwsender, (uint8_t *)&pio->connect.password, sizeof(pio->connect.password), BSSLConnection_GetSendIf(&pio->connect.sslcon), BReactor_PendingGroup(pio->reactor), pio, (SingleStreamSender_handler)pwsender_handler); + + // change state + pio->connect.state = CONNECT_STATE_SENDING; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +void pwsender_handler (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_SENDING) + + // stop using any buffers before they get freed + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->connect.sslcon); + } + + // free password sender + SingleStreamSender_Free(&pio->connect.pwsender); + + if (pio->ssl) { + // free BSSLConnection (we used the send interface) + BSSLConnection_Free(&pio->connect.sslcon); + } else { + // init connection send interface + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + + // change state + pio->connect.state = CONNECT_STATE_SENT; + + // setup i/o + if (!init_io(pio, &pio->connect.sock)) { + goto fail0; + } + + // change state + pio->connect.state = CONNECT_STATE_FINISHED; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +void listener_handler_client (StreamPeerIO *pio, sslsocket *sock) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->mode == MODE_LISTEN) + ASSERT(pio->listen.state == LISTEN_STATE_LISTENER) + + // remember socket + pio->listen.sock = sock; + + // set connection handler + BConnection_SetHandlers(&pio->listen.sock->con, pio, (BConnection_handler)connection_handler); + + // change state + pio->listen.state = LISTEN_STATE_GOTCLIENT; + + // check ceritficate + if (pio->ssl) { + CERTCertificate *peer_cert = SSL_PeerCertificate(pio->listen.sock->ssl_prfd); + if (!peer_cert) { + PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed"); + goto fail0; + } + + // compare certificate to the one provided by the server + if (!compare_certificate(pio, peer_cert)) { + CERT_DestroyCertificate(peer_cert); + goto fail0; + } + + CERT_DestroyCertificate(peer_cert); + } + + // setup i/o + if (!init_io(pio, pio->listen.sock)) { + goto fail0; + } + + // change state + pio->listen.state = LISTEN_STATE_FINISHED; + + return; + +fail0: + reset_and_report_error(pio); + return; +} + +int init_io (StreamPeerIO *pio, sslsocket *sock) +{ + ASSERT(!pio->sock) + + // limit socket send buffer, else our scheduling is pointless + if (pio->sock_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&sock->con, pio->sock_sndbuf)) { + PeerLog(pio, BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + if (pio->ssl) { + // init BSSLConnection + BSSLConnection_Init(&pio->sslcon, sock->ssl_prfd, 0, BReactor_PendingGroup(pio->reactor), pio, (BSSLConnection_handler)sslcon_handler); + } else { + // init connection interfaces + BConnection_SendAsync_Init(&sock->con); + BConnection_RecvAsync_Init(&sock->con); + } + + StreamPassInterface *send_if = (pio->ssl ? BSSLConnection_GetSendIf(&pio->sslcon) : BConnection_SendAsync_GetIf(&sock->con)); + StreamRecvInterface *recv_if = (pio->ssl ? BSSLConnection_GetRecvIf(&pio->sslcon) : BConnection_RecvAsync_GetIf(&sock->con)); + + // init receiving + StreamRecvConnector_ConnectInput(&pio->input_connector, recv_if); + + // init sending + PacketStreamSender_Init(&pio->output_pss, send_if, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor)); + PacketPassConnector_ConnectOutput(&pio->output_connector, PacketStreamSender_GetInput(&pio->output_pss)); + + pio->sock = sock; + + return 1; +} + +void free_io (StreamPeerIO *pio) +{ + ASSERT(pio->sock) + + // stop using any buffers before they get freed + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->sslcon); + } + + // reset decoder + PacketProtoDecoder_Reset(&pio->input_decoder); + + // free sending + PacketPassConnector_DisconnectOutput(&pio->output_connector); + PacketStreamSender_Free(&pio->output_pss); + + // free receiving + StreamRecvConnector_DisconnectInput(&pio->input_connector); + + if (pio->ssl) { + // free BSSLConnection + BSSLConnection_Free(&pio->sslcon); + } else { + // free connection interfaces + BConnection_RecvAsync_Free(&pio->sock->con); + BConnection_SendAsync_Free(&pio->sock->con); + } + + pio->sock = NULL; +} + +void sslcon_handler (StreamPeerIO *pio, int event) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT || pio->mode == MODE_LISTEN) + ASSERT(!(pio->mode == MODE_CONNECT) || pio->connect.state == CONNECT_STATE_FINISHED) + ASSERT(!(pio->mode == MODE_LISTEN) || pio->listen.state == LISTEN_STATE_FINISHED) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + PeerLog(pio, BLOG_NOTICE, "SSL error"); + + reset_and_report_error(pio); + return; +} + +SECStatus client_auth_certificate_callback (StreamPeerIO *pio, PRFileDesc *fd, PRBool checkSig, PRBool isServer) +{ + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + DebugObject_Access(&pio->d_obj); + + // This callback is used to bypass checking the server's domain name, as peers + // don't have domain names. We byte-compare the certificate to the one reported + // by the server anyway. + + SECStatus ret = SECFailure; + + CERTCertificate *server_cert = SSL_PeerCertificate(pio->connect.sock.ssl_prfd); + if (!server_cert) { + PeerLog(pio, BLOG_ERROR, "SSL_PeerCertificate failed"); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail1; + } + + if (CERT_VerifyCertNow(CERT_GetDefaultCertDB(), server_cert, PR_TRUE, certUsageSSLServer, SSL_RevealPinArg(pio->connect.sock.ssl_prfd)) != SECSuccess) { + goto fail2; + } + + // compare to certificate provided by the server + if (!compare_certificate(pio, server_cert)) { + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + goto fail2; + } + + ret = SECSuccess; + +fail2: + CERT_DestroyCertificate(server_cert); +fail1: + return ret; +} + +SECStatus client_client_auth_data_callback (StreamPeerIO *pio, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + ASSERT(pio->ssl) + ASSERT(pio->mode == MODE_CONNECT) + ASSERT(pio->connect.state == CONNECT_STATE_HANDSHAKE) + DebugObject_Access(&pio->d_obj); + + CERTCertificate *cert = CERT_DupCertificate(pio->connect.ssl_cert); + if (!cert) { + PeerLog(pio, BLOG_ERROR, "CERT_DupCertificate failed"); + goto fail0; + } + + SECKEYPrivateKey *key = SECKEY_CopyPrivateKey(pio->connect.ssl_key); + if (!key) { + PeerLog(pio, BLOG_ERROR, "SECKEY_CopyPrivateKey failed"); + goto fail1; + } + + *pRetCert = cert; + *pRetKey = key; + return SECSuccess; + +fail1: + CERT_DestroyCertificate(cert); +fail0: + return SECFailure; +} + +int compare_certificate (StreamPeerIO *pio, CERTCertificate *cert) +{ + ASSERT(pio->ssl) + + SECItem der = cert->derCert; + if (der.len != pio->ssl_peer_cert_len || memcmp(der.data, pio->ssl_peer_cert, der.len)) { + PeerLog(pio, BLOG_NOTICE, "Client certificate doesn't match"); + return 0; + } + + return 1; +} + +void reset_state (StreamPeerIO *pio) +{ + // free resources + switch (pio->mode) { + case MODE_NONE: + break; + case MODE_LISTEN: + switch (pio->listen.state) { + case LISTEN_STATE_FINISHED: + free_io(pio); + case LISTEN_STATE_GOTCLIENT: + if (pio->ssl) { + ASSERT_FORCE(PR_Close(pio->listen.sock->ssl_prfd) == PR_SUCCESS) + BConnection_RecvAsync_Free(&pio->listen.sock->con); + BConnection_SendAsync_Free(&pio->listen.sock->con); + } + BConnection_Free(&pio->listen.sock->con); + free(pio->listen.sock); + case LISTEN_STATE_LISTENER: + if (pio->listen.state == LISTEN_STATE_LISTENER) { + PasswordListener_RemoveEntry(pio->listen.listener, &pio->listen.pwentry); + } + break; + default: + ASSERT(0); + } + break; + case MODE_CONNECT: + switch (pio->connect.state) { + case CONNECT_STATE_FINISHED: + free_io(pio); + case CONNECT_STATE_SENT: + case CONNECT_STATE_SENDING: + if (pio->connect.state == CONNECT_STATE_SENDING) { + if (pio->ssl) { + BSSLConnection_ReleaseBuffers(&pio->connect.sslcon); + } + SingleStreamSender_Free(&pio->connect.pwsender); + if (!pio->ssl) { + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + } + case CONNECT_STATE_HANDSHAKE: + if (pio->ssl) { + if (pio->connect.state == CONNECT_STATE_HANDSHAKE || pio->connect.state == CONNECT_STATE_SENDING) { + BSSLConnection_Free(&pio->connect.sslcon); + } + ASSERT_FORCE(PR_Close(pio->connect.sock.ssl_prfd) == PR_SUCCESS) + BConnection_RecvAsync_Free(&pio->connect.sock.con); + BConnection_SendAsync_Free(&pio->connect.sock.con); + } + BConnection_Free(&pio->connect.sock.con); + case CONNECT_STATE_CONNECTING: + BConnector_Free(&pio->connect.connector); + break; + default: + ASSERT(0); + } + break; + default: + ASSERT(0); + } + + // set mode none + pio->mode = MODE_NONE; + + ASSERT(!pio->sock) +} + +void reset_and_report_error (StreamPeerIO *pio) +{ + reset_state(pio); + + pio->handler_error(pio->user); + return; +} + +int StreamPeerIO_Init ( + StreamPeerIO *pio, + BReactor *reactor, + BThreadWorkDispatcher *twd, + int ssl, + int ssl_flags, + uint8_t *ssl_peer_cert, + int ssl_peer_cert_len, + int payload_mtu, + int sock_sndbuf, + PacketPassInterface *user_recv_if, + BLog_logfunc logfunc, + StreamPeerIO_handler_error handler_error, + void *user +) +{ + ASSERT(ssl == 0 || ssl == 1) + ASSERT(payload_mtu >= 0) + ASSERT(PacketPassInterface_GetMTU(user_recv_if) >= payload_mtu) + ASSERT(handler_error) + + // init arguments + pio->reactor = reactor; + pio->twd = twd; + pio->ssl = ssl; + if (pio->ssl) { + pio->ssl_flags = ssl_flags; + pio->ssl_peer_cert = ssl_peer_cert; + pio->ssl_peer_cert_len = ssl_peer_cert_len; + } + pio->payload_mtu = payload_mtu; + pio->sock_sndbuf = sock_sndbuf; + pio->logfunc = logfunc; + pio->handler_error = handler_error; + pio->user = user; + + // check payload MTU + if (pio->payload_mtu > PACKETPROTO_MAXPAYLOAD) { + PeerLog(pio, BLOG_ERROR, "payload MTU is too large"); + goto fail0; + } + + // init receiveing objects + StreamRecvConnector_Init(&pio->input_connector, BReactor_PendingGroup(pio->reactor)); + if (!PacketProtoDecoder_Init(&pio->input_decoder, StreamRecvConnector_GetOutput(&pio->input_connector), user_recv_if, BReactor_PendingGroup(pio->reactor), pio, + (PacketProtoDecoder_handler_error)decoder_handler_error + )) { + PeerLog(pio, BLOG_ERROR, "FlowErrorDomain_Init failed"); + goto fail1; + } + + // init sending objects + PacketCopier_Init(&pio->output_user_copier, pio->payload_mtu, BReactor_PendingGroup(pio->reactor)); + PacketProtoEncoder_Init(&pio->output_user_ppe, PacketCopier_GetOutput(&pio->output_user_copier), BReactor_PendingGroup(pio->reactor)); + PacketPassConnector_Init(&pio->output_connector, PACKETPROTO_ENCLEN(pio->payload_mtu), BReactor_PendingGroup(pio->reactor)); + if (!SinglePacketBuffer_Init(&pio->output_user_spb, PacketProtoEncoder_GetOutput(&pio->output_user_ppe), PacketPassConnector_GetInput(&pio->output_connector), BReactor_PendingGroup(pio->reactor))) { + PeerLog(pio, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // set mode none + pio->mode = MODE_NONE; + + // set no socket + pio->sock = NULL; + + DebugObject_Init(&pio->d_obj); + return 1; + +fail2: + PacketPassConnector_Free(&pio->output_connector); + PacketProtoEncoder_Free(&pio->output_user_ppe); + PacketCopier_Free(&pio->output_user_copier); + PacketProtoDecoder_Free(&pio->input_decoder); +fail1: + StreamRecvConnector_Free(&pio->input_connector); +fail0: + return 0; +} + +void StreamPeerIO_Free (StreamPeerIO *pio) +{ + DebugObject_Free(&pio->d_obj); + + // reset state + reset_state(pio); + + // free sending objects + SinglePacketBuffer_Free(&pio->output_user_spb); + PacketPassConnector_Free(&pio->output_connector); + PacketProtoEncoder_Free(&pio->output_user_ppe); + PacketCopier_Free(&pio->output_user_copier); + + // free receiveing objects + PacketProtoDecoder_Free(&pio->input_decoder); + StreamRecvConnector_Free(&pio->input_connector); +} + +PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio) +{ + DebugObject_Access(&pio->d_obj); + + return PacketCopier_GetInput(&pio->output_user_copier); +} + +int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key) +{ + DebugObject_Access(&pio->d_obj); + + // reset state + reset_state(pio); + + // check address + if (!BConnection_AddressSupported(addr)) { + PeerLog(pio, BLOG_ERROR, "BConnection_AddressSupported failed"); + goto fail0; + } + + // init connector + if (!BConnector_Init(&pio->connect.connector, addr, pio->reactor, pio, (BConnector_handler)connector_handler)) { + PeerLog(pio, BLOG_ERROR, "BConnector_Init failed"); + goto fail0; + } + + // remember data + if (pio->ssl) { + pio->connect.ssl_cert = ssl_cert; + pio->connect.ssl_key = ssl_key; + } + pio->connect.password = htol64(password); + + // set state + pio->mode = MODE_CONNECT; + pio->connect.state = CONNECT_STATE_CONNECTING; + + return 1; + +fail0: + return 0; +} + +void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password) +{ + DebugObject_Access(&pio->d_obj); + ASSERT(listener->ssl == pio->ssl) + + // reset state + reset_state(pio); + + // add PasswordListener entry + uint64_t newpass = PasswordListener_AddEntry(listener, &pio->listen.pwentry, (PasswordListener_handler_client)listener_handler_client, pio); + + // remember data + pio->listen.listener = listener; + + // set state + pio->mode = MODE_LISTEN; + pio->listen.state = LISTEN_STATE_LISTENER; + + *password = newpass; +} diff --git a/external/badvpn_dns/client/StreamPeerIO.h b/external/badvpn_dns/client/StreamPeerIO.h new file mode 100644 index 00000000..0b6b2601 --- /dev/null +++ b/external/badvpn_dns/client/StreamPeerIO.h @@ -0,0 +1,222 @@ +/** + * @file StreamPeerIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used for communicating with a peer over TCP. + */ + +#ifndef BADVPN_CLIENT_STREAMPEERIO_H +#define BADVPN_CLIENT_STREAMPEERIO_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Callback function invoked when an error occurs with the peer connection. + * The object has entered default state. + * May be called from within a sending Send call. + * + * @param user value given to {@link StreamPeerIO_Init}. + */ +typedef void (*StreamPeerIO_handler_error) (void *user); + +/** + * Object used for communicating with a peer over TCP. + * The object has a logical state which can be one of the following: + * - default state + * - listening state + * - connecting state + */ +typedef struct { + // common arguments + BReactor *reactor; + BThreadWorkDispatcher *twd; + int ssl; + int ssl_flags; + uint8_t *ssl_peer_cert; + int ssl_peer_cert_len; + int payload_mtu; + int sock_sndbuf; + BLog_logfunc logfunc; + StreamPeerIO_handler_error handler_error; + void *user; + + // persistent I/O modules + + // base sending objects + PacketCopier output_user_copier; + PacketProtoEncoder output_user_ppe; + SinglePacketBuffer output_user_spb; + PacketPassConnector output_connector; + + // receiving objects + StreamRecvConnector input_connector; + PacketProtoDecoder input_decoder; + + // connection side + int mode; + + union { + // listening data + struct { + int state; + PasswordListener *listener; + PasswordListener_pwentry pwentry; + sslsocket *sock; + } listen; + // connecting data + struct { + int state; + CERTCertificate *ssl_cert; + SECKEYPrivateKey *ssl_key; + BConnector connector; + sslsocket sock; + BSSLConnection sslcon; + uint64_t password; + SingleStreamSender pwsender; + } connect; + }; + + // socket data + sslsocket *sock; + BSSLConnection sslcon; + + // sending objects + PacketStreamSender output_pss; + + DebugObject d_obj; +} StreamPeerIO; + +/** + * Initializes the object. + * The object is initialized in default state. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSSLConnection_GlobalInit} must have been done if using SSL. + * + * @param pio the object + * @param reactor reactor we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param ssl if nonzero, SSL will be used for peer connection + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param ssl_peer_cert if using SSL, the certificate we expect the peer to have + * @param ssl_peer_cert_len if using SSL, the length of the certificate + * @param payload_mtu maximum packet size as seen from the user. Must be >=0. + * @param sock_sndbuf socket SO_SNDBUF option. Specify <=0 to not set it. + * @param user_recv_if interface to use for submitting received packets. Its MTU + * must be >=payload_mtu. + * @param logfunc function which prepends the log prefix using {@link BLog_Append} + * @param handler_error handler function invoked when a connection error occurs + * @param user value to pass to handler functions + * @return 1 on success, 0 on failure + */ +int StreamPeerIO_Init ( + StreamPeerIO *pio, + BReactor *reactor, + BThreadWorkDispatcher *twd, + int ssl, + int ssl_flags, + uint8_t *ssl_peer_cert, + int ssl_peer_cert_len, + int payload_mtu, + int sock_sndbuf, + PacketPassInterface *user_recv_if, + BLog_logfunc logfunc, + StreamPeerIO_handler_error handler_error, + void *user +) WARN_UNUSED; + +/** + * Frees the object. + * + * @param pio the object + */ +void StreamPeerIO_Free (StreamPeerIO *pio); + +/** + * Returns the interface for sending packets to the peer. + * The OTP warning handler may be called from within Send calls + * to the interface. + * + * @param pio the object + * @return interface for sending packets to the peer + */ +PacketPassInterface * StreamPeerIO_GetSendInput (StreamPeerIO *pio); + +/** + * Starts an attempt to connect to the peer. + * On success, the object enters connecting state. + * On failure, the object enters default state. + * + * @param pio the object + * @param addr address to connect to + * @param password identification code to send to the peer + * @param ssl_cert if using SSL, the client certificate to use. This object does not + * take ownership of the certificate; it must remain valid until + * the object is reset. + * @param ssl_key if using SSL, the private key to use. This object does not take + * ownership of the key; it must remain valid until the object is reset. + * @return 1 on success, 0 on failure + */ +int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERTCertificate *ssl_cert, SECKEYPrivateKey *ssl_key) WARN_UNUSED; + +/** + * Starts an attempt to accept a connection from the peer. + * The object enters listening state. + * + * @param pio the object + * @param listener {@link PasswordListener} object to use for accepting a connection. + * The listener must have SSL enabled if and only if this object has + * SSL enabled. The listener must be available until the object is + * reset or {@link StreamPeerIO_handler_up} is called. + * @param password will return the identification code the peer should send when connecting + */ +void StreamPeerIO_Listen (StreamPeerIO *pio, PasswordListener *listener, uint64_t *password); + +#endif diff --git a/external/badvpn_dns/client/badvpn-client.8 b/external/badvpn_dns/client/badvpn-client.8 new file mode 100644 index 00000000..4f41203d --- /dev/null +++ b/external/badvpn_dns/client/badvpn-client.8 @@ -0,0 +1,316 @@ +.TH badvpn-client 8 "14 July 2011" +.SH NAME +badvpn-client \- VPN node daemon for the BadVPN peer-to-peer VPN system +.SH SYNOPSIS +.B badvpn-client +.RS +.RB "[" --help "]" +.br +.RB "[" --version "]" +.br +.RB "[" --logger " ]" +.br +(logger=syslog? +.br +.RS +.br +.RB "[" --syslog-facility " ]" +.br +.RB "[" --syslog-ident " ]" +.br +.RE +) +.br +.RB "[" --loglevel " <0-5/none/error/warning/notice/info/debug>]" +.br +.RB "[" --channel-loglevel " <0-5/none/error/warning/notice/info/debug>] ..." +.br +.RB "[" --threads " ]" +.br +.RB "[" --ssl " " --nssdb " " --client-cert-name " ]" +.br +.RB "[" --server-name " ]" +.br +.BR --server-addr " " +.br +.RB "[" --tapdev " ]" +.br +.RB "[" --scope " ] ..." +.br +[ +.br +.RS +.BR --bind-addr " " +.br +.RB "(transport-mode=udp? " --num-ports " )" +.br +.RB "[" --ext-addr " ] ..." +.br +.RE +] ... +.br +.BR --transport-mode " " +.br +(transport-mode=udp? +.br +.RS +.BR --encryption-mode " " +.br +.BR --hash-mode " " +.br +.RB "[" --otp " ]" +.br +.RB "[" --fragmentation-latency " ]" +.br +.RE +) +.br +(transport-mode=tcp? +.br +.RS +.RB "(ssl? [" --peer-ssl "])" +.br +.RB "[" --peer-tcp-socket-sndbuf " ]" +.br +.RE +) +.br +.RB "[" --send-buffer-size " ]" +.br +.RB "[" --send-buffer-relay-size " ]" +.br +.RB "[" --max-macs " ]" +.br +.RB "[" --max-groups " ]" +.br +.RB "[" --igmp-group-membership-interval " ]" +.br +.RB "[" --igmp-last-member-query-time " ]" +.br +.RB "[" --allow-peer-talk-without-ssl "]" +.br +.RE +.SH INTRODUCTION +.P +This page documents the BadVPN client, a daemon for a node in a BadVPN VPN network. +For a general description of BadVPN, see +.BR badvpn (7). +.SH DESCRIPTION +.P +The BadVPN client is a daemon that runs on a VPN node. It opens the TAP device, connects to +the server, then keeps running while attempting to establish data connection to peers and +tranferring data between the TAP device and the peers. Once it initializes, the program only +terminates if it loses connection to the server, or if a signal is received. +.SH OPTIONS +.P +The BadVPN client is configured entirely from command line. +.TP +.BR --help +Print version and command line syntax and exit. +.TP +.BR --version +Print version and exit. +.TP +.BR --logger " " +Select where to log messages. Default is stdout. Syslog is not available on Windows. +.TP +.BR --syslog-facility " " +When logging to syslog, set the logging facility. The facility name must be in lower case. +.TP +.BR --syslog-ident " " +When logging to syslog, set the ident. +.TP +.BR --loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the default logging level. +.TP +.BR --channel-loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the logging level for a specific logging channel. +.TP +.BR --threads " " +Hint for the number of additional threads to use for potentionally long computations (such as +encryption and OTP generation). If zero (0) (default), additional threads will be disabled and all +computations will be done in the event loop. If negative (<0), a guess will be made, possibly +based on the number of CPUs. If positive (>0), the given number of threads will be used. +.TP +.BR --ssl +Use TLS. Requires --nssdb and --server-cert-name. +.TP +.BR --nssdb " " +When using TLS, the NSS database to use. Probably something like sql:/some/folder. +.TP +.BR --client-cert-name " " +When using TLS, the name of the certificate to use. The certificate must be readily accessible. +.TP +.BR --server-name " " +Set the name of the server used for validating the server's certificate. The server name defaults +to the the name in the server address (or a numeric address). +.TP +.BR --server-addr " " +Set the address for the server to listen on. See below for address format. +.TP +.BR --tapdev " " +Set the TAP device to use. See below on how to configure the device. A TAP device is a virtual card +in the operating system, but rather than receiving from and sending frames to a piece of hardware, +a program (this one) opens it to read from and write frames into. If the VPN network is set up correctly, +the TAP devices on the VPN nodes will act as if they were all connected into a network switch. +.TP +.BR --scope " " +Add an address scope allowed for connecting to peers. May be specified multiple times to add multiple +scopes. The order of the scopes is irrelevant. Note that it must actually be possible to connect +to addresses in the given scope; when another peer binds for us to connect to, we choose the first +external address whose scope we recognize, and do not attempt further external addresses, even if +establishing the connection fails. +.TP +.BR --bind-addr " " +Add an address to allow binding on. See below for address format. When attempting to bind in order +for some peer to connect to us, the addresses will be tried in the order they are specified. If UDP +data transport is being used, a --num-ports option must follow to specify how many continuous ports +to allow binding to. For the address to be useful, one or more --ext-addr options must follow. +Note that when two peers need to establish a data connection, it is arbitrary which one will attempt +to bind first. +.TP +.BR --num-ports " " +When using UDP transport, set the number of continuous ports for a previously specified bind address. +Must follow a previous --bind-addr option. +.TP +.BR --ext-addr " " +Add an external address for a previously specified bind address. Must follow a previous --bind-addr +option. May be specified multiple times to add multiple external addresses. See below for address +format. Additionally, the IP address part can be {server_reported} to use the IPv4 address as the +server sees us. The external addresses are tried by the connecting peer in the order they are specified. +Note that the connecting peer only attempts to connect to the first address whose scope it recognizes +and does not try other addresses. This means that all addresses must work for be able to communicate. +.TP +.BR --transport-mode " " +Sets the transport protocol for data connections. UDP is recommended and works best for most networks. +TCP can be used instead if the underlying network has high packet loss which your virtual network +cannot tolerate. Must match on all peers. +.TP +.BR --encryption-mode " " +When using UDP transport, sets the encryption mode. None means no encryption, other options mean +a specific cipher. Note that encryption is only useful if clients use TLS to connect to the server. +The encryption mode must match on all peers. +.TP +.BR --hash-mode " " +When using UDP transport, sets the hashing mode. None means no hashes, other options mean a specific +type of hash. Note that hashing is only useful if encryption is used as well. The hash mode must +match on all peers. +.TP +.BR --otp " " +When using UDP transport, enables one-time passwords. The first argument specifies a block cipher +used to generate passwords from a seed. The second argument specifies how many passwords are +generated from a single seed. The third argument specifies after how many passwords used up for +sending packets an attempt is made to negotiate a new seed with the other peer. num must be >0, +and num-warn must be >0 and <=num. The difference (num - num-warn) should be large enough to allow +a new seed to be negotiated before the sender runs out of passwords. Negotiating a seed involves +the sending peer sending it to the receiving peer via the server and the receiving peer confirming +it via the server. Note that one-time passwords are only useful if clients use TLS to connect to the +server. The OTP option must match on all peers, except for num-warn. +.TP +.BR --fragmentation-latency " " +When using UDP transport, sets the maximum latency to sacrifice in order to pack frames into data +packets more efficiently. If it is >=0, a timer of that many milliseconds is used to wait for further +frames to put into an incomplete packet since the first chunk of the packet was written. If it is +<0, packets are sent out immediately. Defaults to 0, which is the recommended setting. +.TP +.BR --peer-ssl +When using TCP transport, enables TLS for data connections. Requires using TLS for server connection. +For this to work, the peers must trust each others' cerificates, and the cerificates must grant the +TLS server usage context. This option must match on all peers. +.TP +.BR --peer-tcp-socket-sndbuf " " +Sets the value of the SO_SNDBUF socket option for peer TCP sockets (zero to not set). Lower values +will improve fairness when data from multiple sources (local and relaying) is being sent to a +given peer, but may result in lower bandwidth if the network's bandwidth-delay product is too big. +.TP +.BR --send-buffer-size " " +Sets the minimum size of the peers' send buffers for sending frames originating from this system, in +number of packets. +.TP +.BR --send-buffer-relay-size " " +Sets the minimum size of the peers' send buffers for relaying frames from other peers, in number of +packets. +.TP +.BR --max-macs " " +Sets the maximum number of MAC addresses to remember for a peer. When the number is exceeded, the least +recently used slot will be reused. +.TP +.BR --max-groups " " +Sets the maximum number of IGMP group memberships to remember for a peer. When the number is exceeded, +the least recently used slot will be reused. +.TP +.BR --igmp-group-membership-interval " " +Sets the Group Membership Interval parameter for IGMP snooping, in milliseconds. +.TP +.BR --igmp-last-member-query-time " " +Sets the Last Member Query Time parameter for IGMP snooping, in milliseconds. +.TP +.BR --allow-peer-talk-without-ssl +When SSL is enabled, the clients not only connect to the server using SSL, but also exchange messages through +the server through another layer of SSL. This protects the messages from attacks on the server. Older versions +of BadVPN (<1.999.109), however, do not support this. This option allows older and newer clients to +interoperate by not using SSL if the other peer does not support it. It does however negate the security +benefits of using SSL, since the (potentionally compromised) server can then order peers not to use SSL. +.SH "EXIT CODE" +.P +If initialization fails, exits with code 1. Otherwise runs until termination is requested or server connection +is broken and exits with code 1. +.SH "ADDRESS FORMAT" +.P +Addresses have the form ipaddr:port, where ipaddr is either an IPv4 address (name or numeric), or an +IPv6 address enclosed in brackets [] (name or numeric again). +.SH "TAP DEVICE CONFIGURATION" +.P +To use this program, you first have to configure a TAP network device that will act as an endpoint for +the virtual network. The configuration depends on your operating system. +.P +Note that the client program does not configure the TAP device in any way; it only reads and writes +frames from/to it. You are responsible for configuring it (e.g. putting it up and setting its IP address). +.P +.B Linux +.P +You need to enable the kernel configuration option CONFIG_TUN. If you enabled it as a module, you may +have to load it (`modprobe tun`) before you can create the device. +.P +Then you should create a persistent TAP device for the VPN client program to open. This can be done with +either the +.B tunctl +or the +.B openvpn +program. The device will be associated with a user account that will have permission to use it, which should +be the same user as the client program will run as (not root!). To create the device with tunctl, use `tunctl -u -t tapN`, +and to create it with openvpn, use `openvpn --mktun --user --dev tapN`, where N is a number that identifies the +TAP device. +.P +Once the TAP device is created, pass `--tapdev tapN` to the client program to make it use this device. Note that the +device will not be preserved across a shutdown of the system; consult your OS documentaton if you want to automate +the creation or configuration of the device. +.P +.B Windows +.P +Windows does not come with a TAP driver. The client program uses the TAP-Win32 driver, which is part of OpenVPN. +You need to install the OpenVPN open source (!) version, and in the installer enable at least the +`TAP Virtual Ethernet Adapter` and `Add Shortcuts to Start Menu` options. +You can get the installer at +.br +. +.P +The OpenVPN installer automatically creates one TAP device on your system when it's run for the first time. +To create another device, use `Programs -> OpenVPN -> Utilities -> Add a new TAP virtual ethernet adapter`. +You may have to install OpenVPN once again to make this shortcut appear. +.P +Once you have a TAP device, you can configure it like a physical network card. You can recognize TAP devices +by their `Device Name` field. +.P +To use the device, pass `--tapdev ":"` to the client program, where is the name of +the TAP driver (tap0901 for OpenVPN 2.1 and 2.2) (case sensitive), and is the (human) name of the TAP +network interface (e.g. `Local Area Connection 2`). +.SH "EXAMPLES" +.P +For examples of using BadVPN, see +.BR badvpn (7). +.SH "SEE ALSO" +.BR badvpn-server (8), +.BR badvpn (7) +.SH AUTHORS +Ambroz Bizjak diff --git a/external/badvpn_dns/client/client.c b/external/badvpn_dns/client/client.c new file mode 100644 index 00000000..2729d6b8 --- /dev/null +++ b/external/badvpn_dns/client/client.c @@ -0,0 +1,2997 @@ +/** + * @file client.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define TRANSPORT_MODE_UDP 0 +#define TRANSPORT_MODE_TCP 1 + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// command-line options +struct ext_addr_option { + char *addr; + char *scope; +}; +struct bind_addr_option { + char *addr; + int num_ports; + int num_ext_addrs; + struct ext_addr_option ext_addrs[MAX_EXT_ADDRS]; +}; +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int threads; + int use_threads_for_ssl_handshake; + int use_threads_for_ssl_data; + int ssl; + char *nssdb; + char *client_cert_name; + char *server_name; + char *server_addr; + char *tapdev; + int num_scopes; + char *scopes[MAX_SCOPES]; + int num_bind_addrs; + struct bind_addr_option bind_addrs[MAX_BIND_ADDRS]; + int transport_mode; + int encryption_mode; + int hash_mode; + int otp_mode; + int otp_num; + int otp_num_warn; + int fragmentation_latency; + int peer_ssl; + int peer_tcp_socket_sndbuf; + int send_buffer_size; + int send_buffer_relay_size; + int max_macs; + int max_groups; + int igmp_group_membership_interval; + int igmp_last_member_query_time; + int allow_peer_talk_without_ssl; + int max_peers; +} options; + +// bind addresses +struct ext_addr { + int server_reported_port; + BAddr addr; // if server_reported_port>=0, defined only after hello received + char scope[64]; +}; +struct bind_addr { + BAddr addr; + int num_ports; + int num_ext_addrs; + struct ext_addr ext_addrs[MAX_EXT_ADDRS]; +}; +int num_bind_addrs; +struct bind_addr bind_addrs[MAX_BIND_ADDRS]; + +// TCP listeners +PasswordListener listeners[MAX_BIND_ADDRS]; + +// SPProto parameters (UDP only) +struct spproto_security_params sp_params; + +// server address we connect to +BAddr server_addr; + +// server name to use for SSL +char server_name[256]; + +// reactor +BReactor ss; + +// thread work dispatcher +BThreadWorkDispatcher twd; + +// client certificate if using SSL +CERTCertificate *client_cert; + +// client private key if using SSL +SECKEYPrivateKey *client_key; + +// device +BTap device; +int device_mtu; + +// DataProtoSource for device input (reading) +DataProtoSource device_dpsource; + +// DPReceiveDevice for device output (writing) +DPReceiveDevice device_output_dprd; + +// data communication MTU +int data_mtu; + +// peers list +LinkedList1 peers; +int num_peers; + +// frame decider +FrameDecider frame_decider; + +// peers that can be user as relays +LinkedList1 relays; + +// peers than need a relay +LinkedList1 waiting_relay_peers; + +// server connection +ServerConnection server; + +// my ID, defined only after server_ready +peerid_t my_id; + +// fair queue for sending peer messages to the server +PacketPassFairQueue server_queue; + +// whether server is ready +int server_ready; + +// dying server flow +struct server_flow *dying_server_flow; + +// stops event processing, causing the program to exit +static void terminate (void); + +// prints program name and version to standard output +static void print_help (const char *name); + +// prints program name and version to standard output +static void print_version (void); + +// parses the command line +static int parse_arguments (int argc, char *argv[]); + +// processes certain command line options +static int process_arguments (void); + +static int ssl_flags (void); + +// handler for program termination request +static void signal_handler (void *unused); + +// adds a new peer +static void peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len); + +// removes a peer +static void peer_remove (struct peer_data *peer, int exiting); + +// appends the peer log prefix to the logger +static void peer_logfunc (struct peer_data *peer); + +// passes a message to the logger, prepending it info about the peer +static void peer_log (struct peer_data *peer, int level, const char *fmt, ...); + +// see if we are the master relative to this peer +static int peer_am_master (struct peer_data *peer); + +// frees PeerChat, disconnecting it from the server flow +static void peer_free_chat (struct peer_data *peer); + +// initializes the link +static int peer_init_link (struct peer_data *peer); + +// frees link resources +static void peer_free_link (struct peer_data *peer); + +// frees link, relaying, waiting relaying +static void peer_cleanup_connections (struct peer_data *peer); + +// registers the peer as a relay provider +static void peer_enable_relay_provider (struct peer_data *peer); + +// unregisters the peer as a relay provider +static void peer_disable_relay_provider (struct peer_data *peer); + +// install relaying for a peer +static void peer_install_relaying (struct peer_data *peer, struct peer_data *relay); + +// uninstall relaying for a peer +static void peer_free_relaying (struct peer_data *peer); + +// handle a peer that needs a relay +static void peer_need_relay (struct peer_data *peer); + +// inserts the peer into the need relay list +static void peer_register_need_relay (struct peer_data *peer); + +// removes the peer from the need relay list +static void peer_unregister_need_relay (struct peer_data *peer); + +// handle a link setup failure +static void peer_reset (struct peer_data *peer); + +// fees chat and sends resetpeer +static void peer_resetpeer (struct peer_data *peer); + +// chat handlers +static void peer_chat_handler_error (struct peer_data *peer); +static void peer_chat_handler_message (struct peer_data *peer, uint8_t *data, int data_len); + +// handlers for different message types +static void peer_msg_youconnect (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_cannotconnect (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_cannotbind (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_seed (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_confirmseed (struct peer_data *peer, uint8_t *data, int data_len); +static void peer_msg_youretry (struct peer_data *peer, uint8_t *data, int data_len); + +// handler from DatagramPeerIO when we should generate a new OTP send seed +static void peer_udp_pio_handler_seed_warning (struct peer_data *peer); + +// handler from DatagramPeerIO when a new OTP seed can be recognized once it was provided to it +static void peer_udp_pio_handler_seed_ready (struct peer_data *peer); + +// handler from DatagramPeerIO when an error occurs on the connection +static void peer_udp_pio_handler_error (struct peer_data *peer); + +// handler from StreamPeerIO when an error occurs on the connection +static void peer_tcp_pio_handler_error (struct peer_data *peer); + +// peer retry timer handler. The timer is used only on the master side, +// wither when we detect an error, or the peer reports an error. +static void peer_reset_timer_handler (struct peer_data *peer); + +// start binding, according to the protocol +static void peer_start_binding (struct peer_data *peer); + +// tries binding on one address, according to the protocol +static void peer_bind (struct peer_data *peer); + +static void peer_bind_one_address (struct peer_data *peer, int addr_index, int *cont); + +static void peer_connect (struct peer_data *peer, BAddr addr, uint8_t *encryption_key, uint64_t password); + +static int peer_start_msg (struct peer_data *peer, void **data, int type, int len); + +static void peer_end_msg (struct peer_data *peer); + +// sends a message with no payload to the peer +static void peer_send_simple (struct peer_data *peer, int msgid); + +static void peer_send_conectinfo (struct peer_data *peer, int addr_index, int port_adjust, uint8_t *enckey, uint64_t pass); + +static void peer_send_confirmseed (struct peer_data *peer, uint16_t seed_id); + +// handler for peer DataProto up state changes +static void peer_dataproto_handler (struct peer_data *peer, int up); + +// looks for a peer with the given ID +static struct peer_data * find_peer_by_id (peerid_t id); + +// device error handler +static void device_error_handler (void *unused); + +// DataProtoSource handler for packets from the device +static void device_dpsource_handler (void *unused, const uint8_t *frame, int frame_len); + +// assign relays to clients waiting for them +static void assign_relays (void); + +// checks if the given address scope is known (i.e. we can connect to an address in it) +static char * address_scope_known (uint8_t *name, int name_len); + +// handlers for server messages +static void server_handler_error (void *user); +static void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip); +static void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); +static void server_handler_endclient (void *user, peerid_t peer_id); +static void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +// jobs +static void peer_job_send_seed (struct peer_data *peer); +static void peer_job_init (struct peer_data *peer); + +// server flows +static struct server_flow * server_flow_init (void); +static void server_flow_free (struct server_flow *flow); +static void server_flow_die (struct server_flow *flow); +static void server_flow_qflow_handler_busy (struct server_flow *flow); +static void server_flow_connect (struct server_flow *flow, PacketRecvInterface *input); +static void server_flow_disconnect (struct server_flow *flow); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + if (options.ssl) { + // init NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // register local NSPR file types + if (!DummyPRFileDesc_GlobalInit()) { + BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed"); + goto fail01; + } + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail01; + } + + // init NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail01; + } + + // set cipher policy + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // init server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.client_cert_name, &client_cert, &client_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail03; + } + } + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // init thread work dispatcher + if (!BThreadWorkDispatcher_Init(&twd, &ss, options.threads)) { + BLog(BLOG_ERROR, "BThreadWorkDispatcher_Init failed"); + goto fail3; + } + + // init BSecurity + if (BThreadWorkDispatcher_UsingThreads(&twd)) { + if (!BSecurity_GlobalInitThreadSafe()) { + BLog(BLOG_ERROR, "BSecurity_GlobalInitThreadSafe failed"); + goto fail4; + } + } + + // init listeners + int num_listeners = 0; + if (options.transport_mode == TRANSPORT_MODE_TCP) { + while (num_listeners < num_bind_addrs) { + struct bind_addr *addr = &bind_addrs[num_listeners]; + if (!PasswordListener_Init(&listeners[num_listeners], &ss, &twd, addr->addr, TCP_MAX_PASSWORD_LISTENER_CLIENTS, options.peer_ssl, ssl_flags(), client_cert, client_key)) { + BLog(BLOG_ERROR, "PasswordListener_Init failed"); + goto fail8; + } + num_listeners++; + } + } + + // init device + if (!BTap_Init(&device, &ss, options.tapdev, device_error_handler, NULL, 0)) { + BLog(BLOG_ERROR, "BTap_Init failed"); + goto fail8; + } + + // remember device MTU + device_mtu = BTap_GetMTU(&device); + + BLog(BLOG_INFO, "device MTU is %d", device_mtu); + + // calculate data MTU + if (device_mtu > INT_MAX - DATAPROTO_MAX_OVERHEAD) { + BLog(BLOG_ERROR, "Device MTU is too large"); + goto fail9; + } + data_mtu = DATAPROTO_MAX_OVERHEAD + device_mtu; + + // init device input + if (!DataProtoSource_Init(&device_dpsource, BTap_GetOutput(&device), device_dpsource_handler, NULL, &ss)) { + BLog(BLOG_ERROR, "DataProtoSource_Init failed"); + goto fail9; + } + + // init device output + if (!DPReceiveDevice_Init(&device_output_dprd, device_mtu, (DPReceiveDevice_output_func)BTap_Send, &device, &ss, options.send_buffer_relay_size, PEER_RELAY_FLOW_INACTIVITY_TIME)) { + BLog(BLOG_ERROR, "DPReceiveDevice_Init failed"); + goto fail10; + } + + // init peers list + LinkedList1_Init(&peers); + num_peers = 0; + + // init frame decider + FrameDecider_Init(&frame_decider, options.max_macs, options.max_groups, options.igmp_group_membership_interval, options.igmp_last_member_query_time, &ss); + + // init relays list + LinkedList1_Init(&relays); + + // init need relay list + LinkedList1_Init(&waiting_relay_peers); + + // start connecting to server + if (!ServerConnection_Init(&server, &ss, &twd, server_addr, SC_KEEPALIVE_INTERVAL, SERVER_BUFFER_MIN_PACKETS, options.ssl, ssl_flags(), client_cert, client_key, server_name, NULL, + server_handler_error, server_handler_ready, server_handler_newclient, server_handler_endclient, server_handler_message + )) { + BLog(BLOG_ERROR, "ServerConnection_Init failed"); + goto fail11; + } + + // set server not ready + server_ready = 0; + + // set no dying flow + dying_server_flow = NULL; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + if (server_ready) { + // allow freeing server queue flows + PacketPassFairQueue_PrepareFree(&server_queue); + + // make ServerConnection stop using buffers from peers before they are freed + ServerConnection_ReleaseBuffers(&server); + } + + // free peers + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&peers)) { + struct peer_data *peer = UPPER_OBJECT(node, struct peer_data, list_node); + peer_remove(peer, 1); + } + + // free dying server flow + if (dying_server_flow) { + server_flow_free(dying_server_flow); + } + + if (server_ready) { + PacketPassFairQueue_Free(&server_queue); + } + ServerConnection_Free(&server); +fail11: + FrameDecider_Free(&frame_decider); + DPReceiveDevice_Free(&device_output_dprd); +fail10: + DataProtoSource_Free(&device_dpsource); +fail9: + BTap_Free(&device); +fail8: + if (options.transport_mode == TRANSPORT_MODE_TCP) { + while (num_listeners-- > 0) { + PasswordListener_Free(&listeners[num_listeners]); + } + } + if (BThreadWorkDispatcher_UsingThreads(&twd)) { + BSecurity_GlobalFreeThreadSafe(); + } +fail4: + // NOTE: BThreadWorkDispatcher must be freed before NSPR and stuff + BThreadWorkDispatcher_Free(&twd); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&ss); +fail1: + if (options.ssl) { + CERT_DestroyCertificate(client_cert); + SECKEY_DestroyPrivateKey(client_key); +fail03: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail02: + SSL_ClearSessionCache(); + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail01: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish objects + DebugObjectGlobal_Finish(); + return 1; +} + +void terminate (void) +{ + BLog(BLOG_NOTICE, "tearing down"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--threads ]\n" + " [--use-threads-for-ssl-handshake]\n" + " [--use-threads-for-ssl-data]\n" + " [--ssl --nssdb --client-cert-name ]\n" + " [--server-name ]\n" + " --server-addr \n" + " [--tapdev ]\n" + " [--scope ] ...\n" + " [\n" + " --bind-addr \n" + " (transport-mode=udp? --num-ports )\n" + " [--ext-addr ] ...\n" + " ] ...\n" + " --transport-mode \n" + " (transport-mode=udp?\n" + " --encryption-mode \n" + " --hash-mode \n" + " [--otp ]\n" + " [--fragmentation-latency ]\n" + " )\n" + " (transport-mode=tcp?\n" + " (ssl? [--peer-ssl])\n" + " [--peer-tcp-socket-sndbuf ]\n" + " )\n" + " [--send-buffer-size ]\n" + " [--send-buffer-relay-size ]\n" + " [--max-macs ]\n" + " [--max-groups ]\n" + " [--igmp-group-membership-interval ]\n" + " [--igmp-last-member-query-time ]\n" + " [--allow-peer-talk-without-ssl]\n" + " [--max-peers ]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.threads = 0; + options.use_threads_for_ssl_handshake = 0; + options.use_threads_for_ssl_data = 0; + options.ssl = 0; + options.nssdb = NULL; + options.client_cert_name = NULL; + options.server_name = NULL; + options.server_addr = NULL; + options.tapdev = NULL; + options.num_scopes = 0; + options.num_bind_addrs = 0; + options.transport_mode = -1; + options.encryption_mode = -1; + options.hash_mode = -1; + options.otp_mode = SPPROTO_OTP_MODE_NONE; + options.fragmentation_latency = PEER_DEFAULT_UDP_FRAGMENTATION_LATENCY; + options.peer_ssl = 0; + options.peer_tcp_socket_sndbuf = -1; + options.send_buffer_size = PEER_DEFAULT_SEND_BUFFER_SIZE; + options.send_buffer_relay_size = PEER_DEFAULT_SEND_BUFFER_RELAY_SIZE; + options.max_macs = PEER_DEFAULT_MAX_MACS; + options.max_groups = PEER_DEFAULT_MAX_GROUPS; + options.igmp_group_membership_interval = DEFAULT_IGMP_GROUP_MEMBERSHIP_INTERVAL; + options.igmp_last_member_query_time = DEFAULT_IGMP_LAST_MEMBER_QUERY_TIME; + options.allow_peer_talk_without_ssl = 0; + options.max_peers = DEFAULT_MAX_PEERS; + + int have_fragmentation_latency = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--threads")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.threads = atoi(argv[i + 1]); + i++; + } + else if (!strcmp(arg, "--use-threads-for-ssl-handshake")) { + options.use_threads_for_ssl_handshake = 1; + } + else if (!strcmp(arg, "--use-threads-for-ssl-data")) { + options.use_threads_for_ssl_data = 1; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.client_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--tapdev")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.tapdev = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--scope")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_scopes == MAX_SCOPES) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.scopes[options.num_scopes] = argv[i + 1]; + options.num_scopes++; + i++; + } + else if (!strcmp(arg, "--bind-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_bind_addrs == MAX_BIND_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs]; + addr->addr = argv[i + 1]; + addr->num_ports = -1; + addr->num_ext_addrs = 0; + options.num_bind_addrs++; + i++; + } + else if (!strcmp(arg, "--num-ports")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_bind_addrs == 0) { + fprintf(stderr, "%s: must folow --bind-addr\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs - 1]; + if ((addr->num_ports = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--ext-addr")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + if (options.num_bind_addrs == 0) { + fprintf(stderr, "%s: must folow --bind-addr\n", arg); + return 0; + } + struct bind_addr_option *addr = &options.bind_addrs[options.num_bind_addrs - 1]; + if (addr->num_ext_addrs == MAX_EXT_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + struct ext_addr_option *eaddr = &addr->ext_addrs[addr->num_ext_addrs]; + eaddr->addr = argv[i + 1]; + eaddr->scope = argv[i + 2]; + addr->num_ext_addrs++; + i += 2; + } + else if (!strcmp(arg, "--transport-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "udp")) { + options.transport_mode = TRANSPORT_MODE_UDP; + } + else if (!strcmp(arg2, "tcp")) { + options.transport_mode = TRANSPORT_MODE_TCP; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--encryption-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "none")) { + options.encryption_mode = SPPROTO_ENCRYPTION_MODE_NONE; + } + else if (!strcmp(arg2, "blowfish")) { + options.encryption_mode = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(arg2, "aes")) { + options.encryption_mode = BENCRYPTION_CIPHER_AES; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--hash-mode")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "none")) { + options.hash_mode = SPPROTO_HASH_MODE_NONE; + } + else if (!strcmp(arg2, "md5")) { + options.hash_mode = BHASH_TYPE_MD5; + } + else if (!strcmp(arg2, "sha1")) { + options.hash_mode = BHASH_TYPE_SHA1; + } + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--otp")) { + if (3 >= argc - i) { + fprintf(stderr, "%s: requires three arguments\n", arg); + return 0; + } + char *otp_mode = argv[i + 1]; + char *otp_num = argv[i + 2]; + char *otp_num_warn = argv[i + 3]; + if (!strcmp(otp_mode, "blowfish")) { + options.otp_mode = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(otp_mode, "aes")) { + options.otp_mode = BENCRYPTION_CIPHER_AES; + } + else { + fprintf(stderr, "%s: wrong mode\n", arg); + return 0; + } + if ((options.otp_num = atoi(otp_num)) <= 0) { + fprintf(stderr, "%s: wrong num\n", arg); + return 0; + } + options.otp_num_warn = atoi(otp_num_warn); + if (options.otp_num_warn <= 0 || options.otp_num_warn > options.otp_num) { + fprintf(stderr, "%s: wrong num warn\n", arg); + return 0; + } + i += 3; + } + else if (!strcmp(arg, "--fragmentation-latency")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.fragmentation_latency = atoi(argv[i + 1]); + have_fragmentation_latency = 1; + i++; + } + else if (!strcmp(arg, "--peer-ssl")) { + options.peer_ssl = 1; + } + else if (!strcmp(arg, "--peer-tcp-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.peer_tcp_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--send-buffer-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.send_buffer_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--send-buffer-relay-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.send_buffer_relay_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-macs")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_macs = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-groups")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_groups = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--igmp-group-membership-interval")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.igmp_group_membership_interval = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--igmp-last-member-query-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.igmp_last_member_query_time = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-peers")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_peers = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--allow-peer-talk-without-ssl")) { + options.allow_peer_talk_without_ssl = 1; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.ssl != !!options.nssdb) { + fprintf(stderr, "False: --ssl <=> --nssdb\n"); + return 0; + } + + if (options.ssl != !!options.client_cert_name) { + fprintf(stderr, "False: --ssl <=> --client-cert-name\n"); + return 0; + } + + if (!options.server_addr) { + fprintf(stderr, "False: --server-addr\n"); + return 0; + } + + if (options.transport_mode < 0) { + fprintf(stderr, "False: --transport-mode\n"); + return 0; + } + + if ((options.transport_mode == TRANSPORT_MODE_UDP) != (options.encryption_mode >= 0)) { + fprintf(stderr, "False: UDP <=> --encryption-mode\n"); + return 0; + } + + if ((options.transport_mode == TRANSPORT_MODE_UDP) != (options.hash_mode >= 0)) { + fprintf(stderr, "False: UDP <=> --hash-mode\n"); + return 0; + } + + if (!(!(options.otp_mode != SPPROTO_OTP_MODE_NONE) || (options.transport_mode == TRANSPORT_MODE_UDP))) { + fprintf(stderr, "False: --otp => UDP\n"); + return 0; + } + + if (!(!have_fragmentation_latency || (options.transport_mode == TRANSPORT_MODE_UDP))) { + fprintf(stderr, "False: --fragmentation-latency => UDP\n"); + return 0; + } + + if (!(!options.peer_ssl || (options.ssl && options.transport_mode == TRANSPORT_MODE_TCP))) { + fprintf(stderr, "False: --peer-ssl => (--ssl && TCP)\n"); + return 0; + } + + if (!(!(options.peer_tcp_socket_sndbuf >= 0) || options.transport_mode == TRANSPORT_MODE_TCP)) { + fprintf(stderr, "False: --peer-tcp-socket-sndbuf => TCP\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve server address + ASSERT(options.server_addr) + if (!BAddr_Parse(&server_addr, options.server_addr, server_name, sizeof(server_name))) { + BLog(BLOG_ERROR, "server addr: BAddr_Parse failed"); + return 0; + } + + // override server name if requested + if (options.server_name) { + if (strlen(options.server_name) >= sizeof(server_name)) { + BLog(BLOG_ERROR, "server name: too long"); + return 0; + } + strcpy(server_name, options.server_name); + } + + // resolve bind addresses and external addresses + num_bind_addrs = 0; + for (int i = 0; i < options.num_bind_addrs; i++) { + struct bind_addr_option *addr = &options.bind_addrs[i]; + struct bind_addr *out = &bind_addrs[num_bind_addrs]; + + // read addr + if (!BAddr_Parse(&out->addr, addr->addr, NULL, 0)) { + BLog(BLOG_ERROR, "bind addr: BAddr_Parse failed"); + return 0; + } + + // read num ports + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (addr->num_ports < 0) { + BLog(BLOG_ERROR, "bind addr: num ports missing"); + return 0; + } + out->num_ports = addr->num_ports; + } + else if (addr->num_ports >= 0) { + BLog(BLOG_ERROR, "bind addr: num ports given, but not using UDP"); + return 0; + } + + // read ext addrs + out->num_ext_addrs = 0; + for (int j = 0; j < addr->num_ext_addrs; j++) { + struct ext_addr_option *eaddr = &addr->ext_addrs[j]; + struct ext_addr *eout = &out->ext_addrs[out->num_ext_addrs]; + + // read addr + if (string_begins_with(eaddr->addr, "{server_reported}:")) { + char *colon = strstr(eaddr->addr, ":"); + if ((eout->server_reported_port = atoi(colon + 1)) < 0) { + BLog(BLOG_ERROR, "ext addr: wrong port"); + return 0; + } + } else { + eout->server_reported_port = -1; + if (!BAddr_Parse(&eout->addr, eaddr->addr, NULL, 0)) { + BLog(BLOG_ERROR, "ext addr: BAddr_Parse failed"); + return 0; + } + if (!addr_supported(eout->addr)) { + BLog(BLOG_ERROR, "ext addr: addr_supported failed"); + return 0; + } + } + + // read scope + if (strlen(eaddr->scope) >= sizeof(eout->scope)) { + BLog(BLOG_ERROR, "ext addr: too long"); + return 0; + } + strcpy(eout->scope, eaddr->scope); + + out->num_ext_addrs++; + } + + num_bind_addrs++; + } + + // initialize SPProto parameters + if (options.transport_mode == TRANSPORT_MODE_UDP) { + sp_params.encryption_mode = options.encryption_mode; + sp_params.hash_mode = options.hash_mode; + sp_params.otp_mode = options.otp_mode; + if (options.otp_mode > 0) { + sp_params.otp_num = options.otp_num; + } + } + + return 1; +} + +int ssl_flags (void) +{ + int flags = 0; + if (options.use_threads_for_ssl_handshake) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE; + } + if (options.use_threads_for_ssl_data) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_IO; + } + return flags; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +void peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + ASSERT(num_peers < options.max_peers) + ASSERT(!find_peer_by_id(id)) + ASSERT(id != my_id) + ASSERT(cert_len >= 0) + ASSERT(cert_len <= SCID_NEWCLIENT_MAX_CERT_LEN) + + // allocate structure + struct peer_data *peer = (struct peer_data *)malloc(sizeof(*peer)); + if (!peer) { + BLog(BLOG_ERROR, "peer %d: failed to allocate memory", (int)id); + goto fail0; + } + + // remember id + peer->id = id; + + // remember flags + peer->flags = flags; + + // set no common name + peer->common_name = NULL; + + if (options.ssl) { + // remember certificate + memcpy(peer->cert, cert, cert_len); + peer->cert_len = cert_len; + + // make sure that CERT_DecodeCertFromPackage will interpretet the input as raw DER and not base64, + // in which case following workaroud wouldn't help + if (!(cert_len > 0 && (cert[0] & 0x1f) == 0x10)) { + peer_log(peer, BLOG_ERROR, "certificate does not look like DER"); + goto fail1; + } + + // copy the certificate and append it a good load of zero bytes, + // hopefully preventing the crappy CERT_DecodeCertFromPackage from crashing + // by reading past the of its input + uint8_t *certbuf = (uint8_t *)malloc(cert_len + 100); + if (!certbuf) { + peer_log(peer, BLOG_ERROR, "malloc failed"); + goto fail1; + } + memcpy(certbuf, cert, cert_len); + memset(certbuf + cert_len, 0, 100); + + // decode certificate, so we can extract the common name + CERTCertificate *nsscert = CERT_DecodeCertFromPackage((char *)certbuf, cert_len); + if (!nsscert) { + peer_log(peer, BLOG_ERROR, "CERT_DecodeCertFromPackage failed (%d)", PORT_GetError()); + free(certbuf); + goto fail1; + } + + free(certbuf); + + // remember common name + if (!(peer->common_name = CERT_GetCommonName(&nsscert->subject))) { + peer_log(peer, BLOG_ERROR, "CERT_GetCommonName failed"); + CERT_DestroyCertificate(nsscert); + goto fail1; + } + + CERT_DestroyCertificate(nsscert); + } + + // init and set init job (must be before initing server flow so we can send) + BPending_Init(&peer->job_init, BReactor_PendingGroup(&ss), (BPending_handler)peer_job_init, peer); + BPending_Set(&peer->job_init); + + // init server flow + if (!(peer->server_flow = server_flow_init())) { + peer_log(peer, BLOG_ERROR, "server_flow_init failed"); + goto fail2; + } + + if ((peer->flags & SCID_NEWCLIENT_FLAG_SSL) && !options.ssl) { + peer_log(peer, BLOG_ERROR, "peer requires talking with SSL, but we're not using SSL!?"); + goto fail3; + } + + if (options.ssl && !(peer->flags & SCID_NEWCLIENT_FLAG_SSL) && !options.allow_peer_talk_without_ssl) { + peer_log(peer, BLOG_ERROR, "peer requires talking without SSL, but we don't allow that"); + goto fail3; + } + + // choose chat SSL mode + int chat_ssl_mode = PEERCHAT_SSL_NONE; + if ((peer->flags & SCID_NEWCLIENT_FLAG_SSL)) { + chat_ssl_mode = (peer_am_master(peer) ? PEERCHAT_SSL_SERVER : PEERCHAT_SSL_CLIENT); + } + + // init chat + if (!PeerChat_Init(&peer->chat, peer->id, chat_ssl_mode, ssl_flags(), client_cert, client_key, peer->cert, peer->cert_len, BReactor_PendingGroup(&ss), &twd, peer, + (BLog_logfunc)peer_logfunc, + (PeerChat_handler_error)peer_chat_handler_error, + (PeerChat_handler_message)peer_chat_handler_message + )) { + peer_log(peer, BLOG_ERROR, "PeerChat_Init failed"); + goto fail3; + } + + // set no message + peer->chat_send_msg_len = -1; + + // connect server flow to chat + server_flow_connect(peer->server_flow, PeerChat_GetSendOutput(&peer->chat)); + + // set have chat + peer->have_chat = 1; + + // set have no resetpeer + peer->have_resetpeer = 0; + + // init local flow + if (!DataProtoFlow_Init(&peer->local_dpflow, &device_dpsource, my_id, peer->id, options.send_buffer_size, -1, NULL, NULL)) { + peer_log(peer, BLOG_ERROR, "DataProtoFlow_Init failed"); + goto fail4; + } + + // init frame decider peer + if (!FrameDeciderPeer_Init(&peer->decider_peer, &frame_decider, peer, (BLog_logfunc)peer_logfunc)) { + peer_log(peer, BLOG_ERROR, "FrameDeciderPeer_Init failed"); + goto fail5; + } + + // init receive peer + DPReceivePeer_Init(&peer->receive_peer, &device_output_dprd, peer->id, &peer->decider_peer, !!(peer->flags & SCID_NEWCLIENT_FLAG_RELAY_CLIENT)); + + // have no link + peer->have_link = 0; + + // have no relaying + peer->relaying_peer = NULL; + + // not waiting for relay + peer->waiting_relay = 0; + + // init reset timer + BTimer_Init(&peer->reset_timer, PEER_RETRY_TIME, (BTimer_handler)peer_reset_timer_handler, peer); + + // is not relay server + peer->is_relay = 0; + + // init binding + peer->binding = 0; + + // add to peers list + LinkedList1_Append(&peers, &peer->list_node); + num_peers++; + + switch (chat_ssl_mode) { + case PEERCHAT_SSL_NONE: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in plaintext mode"); + break; + case PEERCHAT_SSL_CLIENT: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in SSL client mode"); + break; + case PEERCHAT_SSL_SERVER: + peer_log(peer, BLOG_INFO, "initialized; talking to peer in SSL server mode"); + break; + } + + return; + +fail5: + DataProtoFlow_Free(&peer->local_dpflow); +fail4: + server_flow_disconnect(peer->server_flow); + PeerChat_Free(&peer->chat); +fail3: + server_flow_free(peer->server_flow); +fail2: + BPending_Free(&peer->job_init); + if (peer->common_name) { + PORT_Free(peer->common_name); + } +fail1: + free(peer); +fail0: + return; +} + +void peer_remove (struct peer_data *peer, int exiting) +{ + peer_log(peer, BLOG_INFO, "removing"); + + // cleanup connections + peer_cleanup_connections(peer); + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + ASSERT(!peer->is_relay) + + // remove from peers list + LinkedList1_Remove(&peers, &peer->list_node); + num_peers--; + + // free reset timer + BReactor_RemoveTimer(&ss, &peer->reset_timer); + + // free receive peer + DPReceivePeer_Free(&peer->receive_peer); + + // free frame decider + FrameDeciderPeer_Free(&peer->decider_peer); + + // free local flow + DataProtoFlow_Free(&peer->local_dpflow); + + // free chat + if (peer->have_chat) { + peer_free_chat(peer); + } + + // free resetpeer + if (peer->have_resetpeer) { + // disconnect resetpeer source from server flow + server_flow_disconnect(peer->server_flow); + + // free resetpeer source + SinglePacketSource_Free(&peer->resetpeer_source); + } + + // free/die server flow + if (exiting || !PacketPassFairQueueFlow_IsBusy(&peer->server_flow->qflow)) { + server_flow_free(peer->server_flow); + } else { + server_flow_die(peer->server_flow); + } + + // free jobs + BPending_Free(&peer->job_init); + + // free common name + if (peer->common_name) { + PORT_Free(peer->common_name); + } + + // free peer structure + free(peer); +} + +void peer_logfunc (struct peer_data *peer) +{ + BLog_Append("peer %d", (int)peer->id); + if (peer->common_name) { + BLog_Append(" (%s)", peer->common_name); + } + BLog_Append(": "); +} + +void peer_log (struct peer_data *peer, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)peer_logfunc, peer, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +int peer_am_master (struct peer_data *peer) +{ + return (my_id > peer->id); +} + +void peer_free_chat (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + + // disconnect chat from server flow + server_flow_disconnect(peer->server_flow); + + // free chat + PeerChat_Free(&peer->chat); + + // set have no chat + peer->have_chat = 0; +} + +int peer_init_link (struct peer_data *peer) +{ + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + ASSERT(!peer->is_relay) + + // init receive receiver + DPReceiveReceiver_Init(&peer->receive_receiver, &peer->receive_peer); + PacketPassInterface *recv_if = DPReceiveReceiver_GetInput(&peer->receive_receiver); + + // init transport-specific link objects + PacketPassInterface *link_if; + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // init DatagramPeerIO + if (!DatagramPeerIO_Init( + &peer->pio.udp.pio, &ss, data_mtu, CLIENT_UDP_MTU, sp_params, + options.fragmentation_latency, PEER_UDP_ASSEMBLER_NUM_FRAMES, recv_if, + options.otp_num_warn, &twd, peer, + (BLog_logfunc)peer_logfunc, + (DatagramPeerIO_handler_error)peer_udp_pio_handler_error, + (DatagramPeerIO_handler_otp_warning)peer_udp_pio_handler_seed_warning, + (DatagramPeerIO_handler_otp_ready)peer_udp_pio_handler_seed_ready + )) { + peer_log(peer, BLOG_ERROR, "DatagramPeerIO_Init failed"); + goto fail1; + } + + if (SPPROTO_HAVE_OTP(sp_params)) { + // init send seed state + peer->pio.udp.sendseed_nextid = 0; + peer->pio.udp.sendseed_sent = 0; + + // init send seed job + BPending_Init(&peer->pio.udp.job_send_seed, BReactor_PendingGroup(&ss), (BPending_handler)peer_job_send_seed, peer); + } + + link_if = DatagramPeerIO_GetSendInput(&peer->pio.udp.pio); + } else { + // init StreamPeerIO + if (!StreamPeerIO_Init( + &peer->pio.tcp.pio, &ss, &twd, options.peer_ssl, ssl_flags(), + (options.peer_ssl ? peer->cert : NULL), + (options.peer_ssl ? peer->cert_len : -1), + data_mtu, + (options.peer_tcp_socket_sndbuf >= 0 ? options.peer_tcp_socket_sndbuf : PEER_DEFAULT_TCP_SOCKET_SNDBUF), + recv_if, + (BLog_logfunc)peer_logfunc, + (StreamPeerIO_handler_error)peer_tcp_pio_handler_error, peer + )) { + peer_log(peer, BLOG_ERROR, "StreamPeerIO_Init failed"); + goto fail1; + } + + link_if = StreamPeerIO_GetSendInput(&peer->pio.tcp.pio); + } + + // init sending + if (!DataProtoSink_Init(&peer->send_dp, &ss, link_if, PEER_KEEPALIVE_INTERVAL, PEER_KEEPALIVE_RECEIVE_TIMER, (DataProtoSink_handler)peer_dataproto_handler, peer)) { + peer_log(peer, BLOG_ERROR, "DataProto_Init failed"); + goto fail2; + } + + // attach local flow to our DataProtoSink + DataProtoFlow_Attach(&peer->local_dpflow, &peer->send_dp); + + // attach receive peer to our DataProtoSink + DPReceivePeer_AttachSink(&peer->receive_peer, &peer->send_dp); + + // set have link + peer->have_link = 1; + + return 1; + +fail2: + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Free(&peer->pio.udp.job_send_seed); + } + DatagramPeerIO_Free(&peer->pio.udp.pio); + } else { + StreamPeerIO_Free(&peer->pio.tcp.pio); + } +fail1: + DPReceiveReceiver_Free(&peer->receive_receiver); + return 0; +} + +void peer_free_link (struct peer_data *peer) +{ + ASSERT(peer->have_link) + ASSERT(!peer->is_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // detach receive peer from our DataProtoSink + DPReceivePeer_DetachSink(&peer->receive_peer); + + // detach local flow from our DataProtoSink + DataProtoFlow_Detach(&peer->local_dpflow); + + // free sending + DataProtoSink_Free(&peer->send_dp); + + // free transport-specific link objects + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Free(&peer->pio.udp.job_send_seed); + } + DatagramPeerIO_Free(&peer->pio.udp.pio); + } else { + StreamPeerIO_Free(&peer->pio.tcp.pio); + } + + // free receive receiver + DPReceiveReceiver_Free(&peer->receive_receiver); + + // set have no link + peer->have_link = 0; +} + +void peer_cleanup_connections (struct peer_data *peer) +{ + if (peer->have_link) { + if (peer->is_relay) { + peer_disable_relay_provider(peer); + } + peer_free_link(peer); + } + else if (peer->relaying_peer) { + peer_free_relaying(peer); + } + else if (peer->waiting_relay) { + peer_unregister_need_relay(peer); + } + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + ASSERT(!peer->is_relay) +} + +void peer_enable_relay_provider (struct peer_data *peer) +{ + ASSERT(peer->have_link) + ASSERT(!peer->is_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // add to relays list + LinkedList1_Append(&relays, &peer->relay_list_node); + + // init users list + LinkedList1_Init(&peer->relay_users); + + // set is relay + peer->is_relay = 1; + + // assign relays + assign_relays(); +} + +void peer_disable_relay_provider (struct peer_data *peer) +{ + ASSERT(peer->is_relay) + + ASSERT(peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->waiting_relay) + + // disconnect relay users + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&peer->relay_users)) { + struct peer_data *relay_user = UPPER_OBJECT(list_node, struct peer_data, relaying_list_node); + ASSERT(relay_user->relaying_peer == peer) + + // disconnect relay user + peer_free_relaying(relay_user); + + // add it to need relay list + peer_register_need_relay(relay_user); + } + + // remove from relays list + LinkedList1_Remove(&relays, &peer->relay_list_node); + + // set is not relay + peer->is_relay = 0; + + // assign relays + assign_relays(); +} + +void peer_install_relaying (struct peer_data *peer, struct peer_data *relay) +{ + ASSERT(!peer->relaying_peer) + ASSERT(!peer->have_link) + ASSERT(!peer->waiting_relay) + ASSERT(relay->is_relay) + + ASSERT(!peer->is_relay) + ASSERT(relay->have_link) + + peer_log(peer, BLOG_INFO, "installing relaying through %d", (int)relay->id); + + // add to relay's users list + LinkedList1_Append(&relay->relay_users, &peer->relaying_list_node); + + // attach local flow to relay + DataProtoFlow_Attach(&peer->local_dpflow, &relay->send_dp); + + // set relaying + peer->relaying_peer = relay; +} + +void peer_free_relaying (struct peer_data *peer) +{ + ASSERT(peer->relaying_peer) + + ASSERT(!peer->have_link) + ASSERT(!peer->waiting_relay) + + struct peer_data *relay = peer->relaying_peer; + ASSERT(relay->is_relay) + ASSERT(relay->have_link) + + peer_log(peer, BLOG_INFO, "uninstalling relaying through %d", (int)relay->id); + + // detach local flow from relay + DataProtoFlow_Detach(&peer->local_dpflow); + + // remove from relay's users list + LinkedList1_Remove(&relay->relay_users, &peer->relaying_list_node); + + // set not relaying + peer->relaying_peer = NULL; +} + +void peer_need_relay (struct peer_data *peer) +{ + ASSERT(!peer->is_relay) + + if (peer->waiting_relay) { + // already waiting for relay, do nothing + return; + } + + if (peer->have_link) { + peer_free_link(peer); + } + else if (peer->relaying_peer) { + peer_free_relaying(peer); + } + + // register the peer as needing a relay + peer_register_need_relay(peer); + + // assign relays + assign_relays(); +} + +void peer_register_need_relay (struct peer_data *peer) +{ + ASSERT(!peer->waiting_relay) + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + + ASSERT(!peer->is_relay) + + // add to need relay list + LinkedList1_Append(&waiting_relay_peers, &peer->waiting_relay_list_node); + + // set waiting relay + peer->waiting_relay = 1; +} + +void peer_unregister_need_relay (struct peer_data *peer) +{ + ASSERT(peer->waiting_relay) + + ASSERT(!peer->have_link) + ASSERT(!peer->relaying_peer) + ASSERT(!peer->is_relay) + + // remove from need relay list + LinkedList1_Remove(&waiting_relay_peers, &peer->waiting_relay_list_node); + + // set not waiting relay + peer->waiting_relay = 0; +} + +void peer_reset (struct peer_data *peer) +{ + peer_log(peer, BLOG_NOTICE, "resetting"); + + // cleanup connections + peer_cleanup_connections(peer); + + if (peer_am_master(peer)) { + // if we're the master, schedule retry + BReactor_SetTimer(&ss, &peer->reset_timer); + } else { + // if we're the slave, report to master + peer_send_simple(peer, MSGID_YOURETRY); + } +} + +void peer_resetpeer (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + ASSERT(!peer->have_resetpeer) + + // free chat + peer_free_chat(peer); + + // build resetpeer packet + struct packetproto_header pp_header; + struct sc_header sc_header; + struct sc_client_resetpeer sc_resetpeer; + pp_header.len = htol16(sizeof(struct sc_header) + sizeof(struct sc_client_resetpeer)); + sc_header.type = htol8(SCID_RESETPEER); + sc_resetpeer.clientid = htol16(peer->id); + memcpy(peer->resetpeer_packet, &pp_header, sizeof(pp_header)); + memcpy(peer->resetpeer_packet + sizeof(pp_header), &sc_header, sizeof(sc_header)); + memcpy(peer->resetpeer_packet + sizeof(pp_header) + sizeof(sc_header), &sc_resetpeer, sizeof(sc_resetpeer)); + + // init resetpeer sourse + SinglePacketSource_Init(&peer->resetpeer_source, peer->resetpeer_packet, sizeof(peer->resetpeer_packet), BReactor_PendingGroup(&ss)); + + // connect server flow to resetpeer source + server_flow_connect(peer->server_flow, SinglePacketSource_GetOutput(&peer->resetpeer_source)); + + // set have resetpeer + peer->have_resetpeer = 1; +} + +void peer_chat_handler_error (struct peer_data *peer) +{ + ASSERT(peer->have_chat) + ASSERT(!peer->have_resetpeer) + + peer_log(peer, BLOG_ERROR, "chat error, sending resetpeer"); + + peer_resetpeer(peer); +} + +void peer_chat_handler_message (struct peer_data *peer, uint8_t *data, int data_len) +{ + ASSERT(peer->have_chat) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // parse message + msgParser parser; + if (!msgParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_NOTICE, "msg: failed to parse"); + return; + } + + // read message + uint16_t type = 0; // to remove warning + ASSERT_EXECUTE(msgParser_Gettype(&parser, &type)) + uint8_t *payload = NULL; // to remove warning + int payload_len = 0; // to remove warning + ASSERT_EXECUTE(msgParser_Getpayload(&parser, &payload, &payload_len)) + + // dispatch according to message type + switch (type) { + case MSGID_YOUCONNECT: + peer_msg_youconnect(peer, payload, payload_len); + return; + case MSGID_CANNOTCONNECT: + peer_msg_cannotconnect(peer, payload, payload_len); + return; + case MSGID_CANNOTBIND: + peer_msg_cannotbind(peer, payload, payload_len); + return; + case MSGID_YOURETRY: + peer_msg_youretry(peer, payload, payload_len); + return; + case MSGID_SEED: + peer_msg_seed(peer, payload, payload_len); + return; + case MSGID_CONFIRMSEED: + peer_msg_confirmseed(peer, payload, payload_len); + return; + default: + BLog(BLOG_NOTICE, "msg: unknown type"); + return; + } +} + +void peer_msg_youconnect (struct peer_data *peer, uint8_t *data, int data_len) +{ + // init parser + msg_youconnectParser parser; + if (!msg_youconnectParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to parse"); + return; + } + + // try addresses + BAddr addr; + while (1) { + // get address message + uint8_t *addrmsg_data; + int addrmsg_len; + if (!msg_youconnectParser_Getaddr(&parser, &addrmsg_data, &addrmsg_len)) { + peer_log(peer, BLOG_NOTICE, "msg_youconnect: no usable addresses"); + peer_send_simple(peer, MSGID_CANNOTCONNECT); + return; + } + + // parse address message + msg_youconnect_addrParser aparser; + if (!msg_youconnect_addrParser_Init(&aparser, addrmsg_data, addrmsg_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to parse address message"); + return; + } + + // check if the address scope is known + uint8_t *name_data = NULL; // to remove warning + int name_len = 0; // to remove warning + ASSERT_EXECUTE(msg_youconnect_addrParser_Getname(&aparser, &name_data, &name_len)) + char *name; + if (!(name = address_scope_known(name_data, name_len))) { + continue; + } + + // read address + uint8_t *addr_data = NULL; // to remove warning + int addr_len = 0; // to remove warning + ASSERT_EXECUTE(msg_youconnect_addrParser_Getaddr(&aparser, &addr_data, &addr_len)) + if (!addr_read(addr_data, addr_len, &addr)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: failed to read address"); + continue; + } + + peer_log(peer, BLOG_NOTICE, "msg_youconnect: using address in scope '%s'", name); + break; + } + + // discard further addresses + msg_youconnectParser_Forwardaddr(&parser); + + uint8_t *key = NULL; + uint64_t password = 0; + + // read additonal parameters + if (options.transport_mode == TRANSPORT_MODE_UDP) { + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + int key_len; + if (!msg_youconnectParser_Getkey(&parser, &key, &key_len)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: no key"); + return; + } + if (key_len != BEncryption_cipher_key_size(sp_params.encryption_mode)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: wrong key size"); + return; + } + } + } else { + if (!msg_youconnectParser_Getpassword(&parser, &password)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: no password"); + return; + } + } + + if (!msg_youconnectParser_GotEverything(&parser)) { + peer_log(peer, BLOG_WARNING, "msg_youconnect: stray data"); + return; + } + + peer_log(peer, BLOG_INFO, "connecting"); + + peer_connect(peer, addr, key, password); +} + +void peer_msg_cannotconnect (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_cannotconnect: invalid length"); + return; + } + + if (!peer->binding) { + peer_log(peer, BLOG_WARNING, "msg_cannotconnect: not binding"); + return; + } + + peer_log(peer, BLOG_INFO, "peer could not connect"); + + // continue trying bind addresses + peer_bind(peer); + return; +} + +void peer_msg_cannotbind (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_cannotbind: invalid length"); + return; + } + + peer_log(peer, BLOG_INFO, "peer cannot bind"); + + if (!peer_am_master(peer)) { + peer_start_binding(peer); + } else { + if (!peer->is_relay) { + peer_need_relay(peer); + } + } +} + +void peer_msg_seed (struct peer_data *peer, uint8_t *data, int data_len) +{ + msg_seedParser parser; + if (!msg_seedParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_seed: failed to parse"); + return; + } + + // read message + uint16_t seed_id = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getseed_id(&parser, &seed_id)) + uint8_t *key = NULL; // to remove warning + int key_len = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getkey(&parser, &key, &key_len)) + uint8_t *iv = NULL; // to remove warning + int iv_len = 0; // to remove warning + ASSERT_EXECUTE(msg_seedParser_Getiv(&parser, &iv, &iv_len)) + + if (options.transport_mode != TRANSPORT_MODE_UDP) { + peer_log(peer, BLOG_WARNING, "msg_seed: not in UDP mode"); + return; + } + + if (!SPPROTO_HAVE_OTP(sp_params)) { + peer_log(peer, BLOG_WARNING, "msg_seed: OTPs disabled"); + return; + } + + if (key_len != BEncryption_cipher_key_size(sp_params.otp_mode)) { + peer_log(peer, BLOG_WARNING, "msg_seed: wrong key length"); + return; + } + + if (iv_len != BEncryption_cipher_block_size(sp_params.otp_mode)) { + peer_log(peer, BLOG_WARNING, "msg_seed: wrong IV length"); + return; + } + + if (!peer->have_link) { + peer_log(peer, BLOG_WARNING, "msg_seed: have no link"); + return; + } + + peer_log(peer, BLOG_DEBUG, "received OTP receive seed"); + + // add receive seed + DatagramPeerIO_AddOTPRecvSeed(&peer->pio.udp.pio, seed_id, key, iv); + + // remember seed ID so we can confirm it from peer_udp_pio_handler_seed_ready + peer->pio.udp.pending_recvseed_id = seed_id; +} + +void peer_msg_confirmseed (struct peer_data *peer, uint8_t *data, int data_len) +{ + msg_confirmseedParser parser; + if (!msg_confirmseedParser_Init(&parser, data, data_len)) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: failed to parse"); + return; + } + + // read message + uint16_t seed_id = 0; // to remove warning + ASSERT_EXECUTE(msg_confirmseedParser_Getseed_id(&parser, &seed_id)) + + if (options.transport_mode != TRANSPORT_MODE_UDP) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: not in UDP mode"); + return; + } + + if (!SPPROTO_HAVE_OTP(sp_params)) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: OTPs disabled"); + return; + } + + if (!peer->have_link) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: have no link"); + return; + } + + if (!peer->pio.udp.sendseed_sent) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: no seed has been sent"); + return; + } + + if (seed_id != peer->pio.udp.sendseed_sent_id) { + peer_log(peer, BLOG_WARNING, "msg_confirmseed: invalid seed: expecting %d, received %d", (int)peer->pio.udp.sendseed_sent_id, (int)seed_id); + return; + } + + peer_log(peer, BLOG_DEBUG, "OTP send seed confirmed"); + + // no longer waiting for confirmation + peer->pio.udp.sendseed_sent = 0; + + // start using the seed + DatagramPeerIO_SetOTPSendSeed(&peer->pio.udp.pio, peer->pio.udp.sendseed_sent_id, peer->pio.udp.sendseed_sent_key, peer->pio.udp.sendseed_sent_iv); +} + +void peer_msg_youretry (struct peer_data *peer, uint8_t *data, int data_len) +{ + if (data_len != 0) { + peer_log(peer, BLOG_WARNING, "msg_youretry: invalid length"); + return; + } + + if (!peer_am_master(peer)) { + peer_log(peer, BLOG_WARNING, "msg_youretry: we are not master"); + return; + } + + peer_log(peer, BLOG_NOTICE, "requests reset"); + + peer_reset(peer); +} + +void peer_udp_pio_handler_seed_warning (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + + // generate and send a new seed + if (!peer->pio.udp.sendseed_sent) { + BPending_Set(&peer->pio.udp.job_send_seed); + } +} + +void peer_udp_pio_handler_seed_ready (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + + // send confirmation + peer_send_confirmseed(peer, peer->pio.udp.pending_recvseed_id); +} + +void peer_udp_pio_handler_error (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(peer->have_link) + + peer_log(peer, BLOG_NOTICE, "UDP connection failed"); + + peer_reset(peer); + return; +} + +void peer_tcp_pio_handler_error (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_TCP) + ASSERT(peer->have_link) + + peer_log(peer, BLOG_NOTICE, "TCP connection failed"); + + peer_reset(peer); + return; +} + +void peer_reset_timer_handler (struct peer_data *peer) +{ + ASSERT(peer_am_master(peer)) + + BLog(BLOG_NOTICE, "retry timer expired"); + + // start setup process + peer_start_binding(peer); +} + +void peer_start_binding (struct peer_data *peer) +{ + peer->binding = 1; + peer->binding_addrpos = 0; + + peer_bind(peer); +} + +void peer_bind (struct peer_data *peer) +{ + ASSERT(peer->binding) + ASSERT(peer->binding_addrpos >= 0) + ASSERT(peer->binding_addrpos <= num_bind_addrs) + + while (peer->binding_addrpos < num_bind_addrs) { + // if there are no external addresses, skip bind address + if (bind_addrs[peer->binding_addrpos].num_ext_addrs == 0) { + peer->binding_addrpos++; + continue; + } + + // try to bind + int cont; + peer_bind_one_address(peer, peer->binding_addrpos, &cont); + + // increment address counter + peer->binding_addrpos++; + + if (!cont) { + return; + } + } + + peer_log(peer, BLOG_NOTICE, "no more addresses to bind to"); + + // no longer binding + peer->binding = 0; + + // tell the peer we failed to bind + peer_send_simple(peer, MSGID_CANNOTBIND); + + // if we are the slave, setup relaying + if (!peer_am_master(peer)) { + if (!peer->is_relay) { + peer_need_relay(peer); + } + } +} + +void peer_bind_one_address (struct peer_data *peer, int addr_index, int *cont) +{ + ASSERT(addr_index >= 0) + ASSERT(addr_index < num_bind_addrs) + ASSERT(bind_addrs[addr_index].num_ext_addrs > 0) + + // get a fresh link + peer_cleanup_connections(peer); + if (!peer_init_link(peer)) { + peer_log(peer, BLOG_ERROR, "cannot get link"); + *cont = 0; + peer_reset(peer); + return; + } + + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // get addr + struct bind_addr *addr = &bind_addrs[addr_index]; + + // try binding to all ports in the range + int port_add; + for (port_add = 0; port_add < addr->num_ports; port_add++) { + BAddr tryaddr = addr->addr; + BAddr_SetPort(&tryaddr, hton16(ntoh16(BAddr_GetPort(&tryaddr)) + port_add)); + if (DatagramPeerIO_Bind(&peer->pio.udp.pio, tryaddr)) { + break; + } + } + if (port_add == addr->num_ports) { + BLog(BLOG_NOTICE, "failed to bind to any port"); + *cont = 1; + return; + } + + uint8_t key[BENCRYPTION_MAX_KEY_SIZE]; + + // generate and set encryption key + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + BRandom_randomize(key, BEncryption_cipher_key_size(sp_params.encryption_mode)); + DatagramPeerIO_SetEncryptionKey(&peer->pio.udp.pio, key); + } + + // schedule sending OTP seed + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Set(&peer->pio.udp.job_send_seed); + } + + // send connectinfo + peer_send_conectinfo(peer, addr_index, port_add, key, 0); + } else { + // order StreamPeerIO to listen + uint64_t pass; + StreamPeerIO_Listen(&peer->pio.tcp.pio, &listeners[addr_index], &pass); + + // send connectinfo + peer_send_conectinfo(peer, addr_index, 0, NULL, pass); + } + + peer_log(peer, BLOG_NOTICE, "bound to address number %d", addr_index); + + *cont = 0; +} + +void peer_connect (struct peer_data *peer, BAddr addr, uint8_t* encryption_key, uint64_t password) +{ + // get a fresh link + peer_cleanup_connections(peer); + if (!peer_init_link(peer)) { + peer_log(peer, BLOG_ERROR, "cannot get link"); + peer_reset(peer); + return; + } + + if (options.transport_mode == TRANSPORT_MODE_UDP) { + // order DatagramPeerIO to connect + if (!DatagramPeerIO_Connect(&peer->pio.udp.pio, addr)) { + peer_log(peer, BLOG_NOTICE, "DatagramPeerIO_Connect failed"); + peer_reset(peer); + return; + } + + // set encryption key + if (SPPROTO_HAVE_ENCRYPTION(sp_params)) { + DatagramPeerIO_SetEncryptionKey(&peer->pio.udp.pio, encryption_key); + } + + // generate and send a send seed + if (SPPROTO_HAVE_OTP(sp_params)) { + BPending_Set(&peer->pio.udp.job_send_seed); + } + } else { + // order StreamPeerIO to connect + if (!StreamPeerIO_Connect(&peer->pio.tcp.pio, addr, password, client_cert, client_key)) { + peer_log(peer, BLOG_NOTICE, "StreamPeerIO_Connect failed"); + peer_reset(peer); + return; + } + } +} + +static int peer_start_msg (struct peer_data *peer, void **data, int type, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= MSG_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + ASSERT(peer->chat_send_msg_len == -1) + + // make sure we have chat + if (!peer->have_chat) { + peer_log(peer, BLOG_ERROR, "cannot send message, chat is down"); + return 0; + } + +#ifdef SIMULATE_PEER_OUT_OF_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_PEER_OUT_OF_BUFFER) { + peer_log(peer, BLOG_ERROR, "simulating out of buffer, sending resetpeer"); + peer_resetpeer(peer); + return 0; + } +#endif + + // obtain buffer location + uint8_t *packet; + if (!PeerChat_StartMessage(&peer->chat, &packet)) { + peer_log(peer, BLOG_ERROR, "cannot send message, out of buffer, sending resetpeer"); + peer_resetpeer(peer); + return 0; + } + + // write fields + msgWriter writer; + msgWriter_Init(&writer, packet); + msgWriter_Addtype(&writer, type); + uint8_t *payload_dst = msgWriter_Addpayload(&writer, len); + msgWriter_Finish(&writer); + + // set have message + peer->chat_send_msg_len = len; + + if (data) { + *data = payload_dst; + } + return 1; +} + +static void peer_end_msg (struct peer_data *peer) +{ + ASSERT(peer->chat_send_msg_len >= 0) + ASSERT(peer->have_chat) + + // submit packet to buffer + PeerChat_EndMessage(&peer->chat, msg_SIZEtype + msg_SIZEpayload(peer->chat_send_msg_len)); + + // set no message + peer->chat_send_msg_len = -1; +} + +void peer_send_simple (struct peer_data *peer, int msgid) +{ + if (!peer_start_msg(peer, NULL, msgid, 0)) { + return; + } + peer_end_msg(peer); +} + +void peer_send_conectinfo (struct peer_data *peer, int addr_index, int port_adjust, uint8_t *enckey, uint64_t pass) +{ + ASSERT(addr_index >= 0) + ASSERT(addr_index < num_bind_addrs) + ASSERT(bind_addrs[addr_index].num_ext_addrs > 0) + + // get address + struct bind_addr *bind_addr = &bind_addrs[addr_index]; + + // remember encryption key size + int key_size = 0; // to remove warning + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + key_size = BEncryption_cipher_key_size(sp_params.encryption_mode); + } + + // calculate message length .. + int msg_len = 0; + + // addresses + for (int i = 0; i < bind_addr->num_ext_addrs; i++) { + int addrmsg_len = + msg_youconnect_addr_SIZEname(strlen(bind_addr->ext_addrs[i].scope)) + + msg_youconnect_addr_SIZEaddr(addr_size(bind_addr->ext_addrs[i].addr)); + msg_len += msg_youconnect_SIZEaddr(addrmsg_len); + } + + // encryption key + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + msg_len += msg_youconnect_SIZEkey(key_size); + } + + // password + if (options.transport_mode == TRANSPORT_MODE_TCP) { + msg_len += msg_youconnect_SIZEpassword; + } + + // check if it's too big (because of the addresses) + if (msg_len > MSG_MAX_PAYLOAD) { + BLog(BLOG_ERROR, "cannot send too big youconnect message"); + return; + } + + // start message + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_YOUCONNECT, msg_len)) { + return; + } + + // init writer + msg_youconnectWriter writer; + msg_youconnectWriter_Init(&writer, msg); + + // write addresses + for (int i = 0; i < bind_addr->num_ext_addrs; i++) { + int name_len = strlen(bind_addr->ext_addrs[i].scope); + int addr_len = addr_size(bind_addr->ext_addrs[i].addr); + + // get a pointer for writing the address + int addrmsg_len = + msg_youconnect_addr_SIZEname(name_len) + + msg_youconnect_addr_SIZEaddr(addr_len); + uint8_t *addrmsg_dst = msg_youconnectWriter_Addaddr(&writer, addrmsg_len); + + // init address writer + msg_youconnect_addrWriter awriter; + msg_youconnect_addrWriter_Init(&awriter, addrmsg_dst); + + // write scope + uint8_t *name_dst = msg_youconnect_addrWriter_Addname(&awriter, name_len); + memcpy(name_dst, bind_addr->ext_addrs[i].scope, name_len); + + // write address with adjusted port + BAddr addr = bind_addr->ext_addrs[i].addr; + BAddr_SetPort(&addr, hton16(ntoh16(BAddr_GetPort(&addr)) + port_adjust)); + uint8_t *addr_dst = msg_youconnect_addrWriter_Addaddr(&awriter, addr_len); + addr_write(addr_dst, addr); + + // finish address writer + msg_youconnect_addrWriter_Finish(&awriter); + } + + // write encryption key + if (options.transport_mode == TRANSPORT_MODE_UDP && SPPROTO_HAVE_ENCRYPTION(sp_params)) { + uint8_t *key_dst = msg_youconnectWriter_Addkey(&writer, key_size); + memcpy(key_dst, enckey, key_size); + } + + // write password + if (options.transport_mode == TRANSPORT_MODE_TCP) { + msg_youconnectWriter_Addpassword(&writer, pass); + } + + // finish writer + msg_youconnectWriter_Finish(&writer); + + // end message + peer_end_msg(peer); +} + +void peer_send_confirmseed (struct peer_data *peer, uint16_t seed_id) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + + // send confirmation + int msg_len = msg_confirmseed_SIZEseed_id; + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_CONFIRMSEED, msg_len)) { + return; + } + msg_confirmseedWriter writer; + msg_confirmseedWriter_Init(&writer, msg); + msg_confirmseedWriter_Addseed_id(&writer, seed_id); + msg_confirmseedWriter_Finish(&writer); + peer_end_msg(peer); +} + +void peer_dataproto_handler (struct peer_data *peer, int up) +{ + ASSERT(peer->have_link) + + if (up) { + peer_log(peer, BLOG_INFO, "up"); + + // if it can be a relay provided, enable it + if ((peer->flags & SCID_NEWCLIENT_FLAG_RELAY_SERVER) && !peer->is_relay) { + peer_enable_relay_provider(peer); + } + } else { + peer_log(peer, BLOG_INFO, "down"); + + // if it is a relay provider, disable it + if (peer->is_relay) { + peer_disable_relay_provider(peer); + } + } +} + +struct peer_data * find_peer_by_id (peerid_t id) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&peers); node; node = LinkedList1Node_Next(node)) { + struct peer_data *peer = UPPER_OBJECT(node, struct peer_data, list_node); + if (peer->id == id) { + return peer; + } + } + + return NULL; +} + +void device_error_handler (void *unused) +{ + BLog(BLOG_ERROR, "device error"); + + terminate(); +} + +void device_dpsource_handler (void *unused, const uint8_t *frame, int frame_len) +{ + ASSERT(frame_len >= 0) + ASSERT(frame_len <= device_mtu) + + // give frame to decider + FrameDecider_AnalyzeAndDecide(&frame_decider, frame, frame_len); + + // forward frame to peers + FrameDeciderPeer *decider_peer = FrameDecider_NextDestination(&frame_decider); + while (decider_peer) { + FrameDeciderPeer *next = FrameDecider_NextDestination(&frame_decider); + struct peer_data *peer = UPPER_OBJECT(decider_peer, struct peer_data, decider_peer); + DataProtoFlow_Route(&peer->local_dpflow, !!next); + decider_peer = next; + } +} + +void assign_relays (void) +{ + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&waiting_relay_peers)) { + struct peer_data *peer = UPPER_OBJECT(list_node, struct peer_data, waiting_relay_list_node); + ASSERT(peer->waiting_relay) + + ASSERT(!peer->relaying_peer) + ASSERT(!peer->have_link) + + // get a relay + LinkedList1Node *list_node2 = LinkedList1_GetFirst(&relays); + if (!list_node2) { + BLog(BLOG_NOTICE, "no relays"); + return; + } + struct peer_data *relay = UPPER_OBJECT(list_node2, struct peer_data, relay_list_node); + ASSERT(relay->is_relay) + + // no longer waiting for relay + peer_unregister_need_relay(peer); + + // install the relay + peer_install_relaying(peer, relay); + } +} + +char * address_scope_known (uint8_t *name, int name_len) +{ + ASSERT(name_len >= 0) + + for (int i = 0; i < options.num_scopes; i++) { + if (name_len == strlen(options.scopes[i]) && !memcmp(name, options.scopes[i], name_len)) { + return options.scopes[i]; + } + } + + return NULL; +} + +void server_handler_error (void *user) +{ + BLog(BLOG_ERROR, "server connection failed, exiting"); + + terminate(); +} + +void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip) +{ + ASSERT(!server_ready) + + // remember our ID + my_id = param_my_id; + + // store server reported addresses + for (int i = 0; i < num_bind_addrs; i++) { + struct bind_addr *addr = &bind_addrs[i]; + for (int j = 0; j < addr->num_ext_addrs; j++) { + struct ext_addr *eaddr = &addr->ext_addrs[j]; + if (eaddr->server_reported_port >= 0) { + if (ext_ip == 0) { + BLog(BLOG_ERROR, "server did not provide our address"); + terminate(); + return; + } + BAddr_InitIPv4(&eaddr->addr, ext_ip, hton16(eaddr->server_reported_port)); + char str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&eaddr->addr, str); + BLog(BLOG_INFO, "external address (%d,%d): server reported %s", i, j, str); + } + } + } + + // give receive device the ID + DPReceiveDevice_SetPeerID(&device_output_dprd, my_id); + + // init server queue + if (!PacketPassFairQueue_Init(&server_queue, ServerConnection_GetSendInterface(&server), BReactor_PendingGroup(&ss), 0, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + terminate(); + return; + } + + // set server ready + server_ready = 1; + + BLog(BLOG_INFO, "server: ready, my ID is %d", (int)my_id); +} + +void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + ASSERT(cert_len >= 0) + ASSERT(cert_len <= SCID_NEWCLIENT_MAX_CERT_LEN) + + // check if the peer already exists + if (find_peer_by_id(peer_id)) { + BLog(BLOG_WARNING, "server: newclient: peer already known"); + return; + } + + // make sure it's not the same ID as us + if (peer_id == my_id) { + BLog(BLOG_WARNING, "server: newclient: peer has our ID"); + return; + } + + // check if there is spece for the peer + if (num_peers >= options.max_peers) { + BLog(BLOG_WARNING, "server: newclient: no space for new peer (maximum number reached)"); + return; + } + + if (!options.ssl && cert_len > 0) { + BLog(BLOG_WARNING, "server: newclient: certificate supplied, but not using TLS"); + return; + } + + peer_add(peer_id, flags, cert, cert_len); +} + +void server_handler_endclient (void *user, peerid_t peer_id) +{ + ASSERT(server_ready) + + // find peer + struct peer_data *peer = find_peer_by_id(peer_id); + if (!peer) { + BLog(BLOG_WARNING, "server: endclient: peer %d not known", (int)peer_id); + return; + } + + // remove peer + peer_remove(peer, 0); +} + +void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len) +{ + ASSERT(server_ready) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + // find peer + struct peer_data *peer = find_peer_by_id(peer_id); + if (!peer) { + BLog(BLOG_WARNING, "server: message: peer not known"); + return; + } + + // make sure we have chat + if (!peer->have_chat) { + peer_log(peer, BLOG_ERROR, "cannot process message, chat is down"); + return; + } + + // pass message to chat + PeerChat_InputReceived(&peer->chat, data, data_len); +} + +void peer_job_send_seed (struct peer_data *peer) +{ + ASSERT(options.transport_mode == TRANSPORT_MODE_UDP) + ASSERT(SPPROTO_HAVE_OTP(sp_params)) + ASSERT(peer->have_link) + ASSERT(!peer->pio.udp.sendseed_sent) + + peer_log(peer, BLOG_DEBUG, "sending OTP send seed"); + + int key_len = BEncryption_cipher_key_size(sp_params.otp_mode); + int iv_len = BEncryption_cipher_block_size(sp_params.otp_mode); + + // generate seed + peer->pio.udp.sendseed_sent_id = peer->pio.udp.sendseed_nextid; + BRandom_randomize(peer->pio.udp.sendseed_sent_key, key_len); + BRandom_randomize(peer->pio.udp.sendseed_sent_iv, iv_len); + + // set as sent, increment next seed ID + peer->pio.udp.sendseed_sent = 1; + peer->pio.udp.sendseed_nextid++; + + // send seed to the peer + int msg_len = msg_seed_SIZEseed_id + msg_seed_SIZEkey(key_len) + msg_seed_SIZEiv(iv_len); + if (msg_len > MSG_MAX_PAYLOAD) { + peer_log(peer, BLOG_ERROR, "OTP send seed message too big"); + return; + } + uint8_t *msg; + if (!peer_start_msg(peer, (void **)&msg, MSGID_SEED, msg_len)) { + return; + } + msg_seedWriter writer; + msg_seedWriter_Init(&writer, msg); + msg_seedWriter_Addseed_id(&writer, peer->pio.udp.sendseed_sent_id); + uint8_t *key_dst = msg_seedWriter_Addkey(&writer, key_len); + memcpy(key_dst, peer->pio.udp.sendseed_sent_key, key_len); + uint8_t *iv_dst = msg_seedWriter_Addiv(&writer, iv_len); + memcpy(iv_dst, peer->pio.udp.sendseed_sent_iv, iv_len); + msg_seedWriter_Finish(&writer); + peer_end_msg(peer); +} + +void peer_job_init (struct peer_data *peer) +{ + // start setup process + if (peer_am_master(peer)) { + peer_start_binding(peer); + } +} + +struct server_flow * server_flow_init (void) +{ + ASSERT(server_ready) + + // allocate structure + struct server_flow *flow = (struct server_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init queue flow + PacketPassFairQueueFlow_Init(&flow->qflow, &server_queue); + + // init connector + PacketRecvConnector_Init(&flow->connector, sizeof(struct packetproto_header) + SC_MAX_ENC, BReactor_PendingGroup(&ss)); + + // init encoder buffer + if (!SinglePacketBuffer_Init(&flow->encoder_buffer, PacketRecvConnector_GetOutput(&flow->connector), PacketPassFairQueueFlow_GetInput(&flow->qflow), BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail1; + } + + // set not connected + flow->connected = 0; + + return flow; + +fail1: + PacketRecvConnector_Free(&flow->connector); + PacketPassFairQueueFlow_Free(&flow->qflow); + free(flow); +fail0: + return NULL; +} + +void server_flow_free (struct server_flow *flow) +{ + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + ASSERT(!flow->connected) + + // remove dying flow reference + if (flow == dying_server_flow) { + dying_server_flow = NULL; + } + + // free encoder buffer + SinglePacketBuffer_Free(&flow->encoder_buffer); + + // free connector + PacketRecvConnector_Free(&flow->connector); + + // free queue flow + PacketPassFairQueueFlow_Free(&flow->qflow); + + // free structure + free(flow); +} + +void server_flow_die (struct server_flow *flow) +{ + ASSERT(PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + ASSERT(!flow->connected) + ASSERT(!dying_server_flow) + + // request notification when flow is done + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)server_flow_qflow_handler_busy, flow); + + // set dying flow + dying_server_flow = flow; +} + +void server_flow_qflow_handler_busy (struct server_flow *flow) +{ + ASSERT(flow == dying_server_flow) + ASSERT(!flow->connected) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + // finally free flow + server_flow_free(flow); +} + +void server_flow_connect (struct server_flow *flow, PacketRecvInterface *input) +{ + ASSERT(!flow->connected) + ASSERT(flow != dying_server_flow) + + // connect input + PacketRecvConnector_ConnectInput(&flow->connector, input); + + // set connected + flow->connected = 1; +} + +void server_flow_disconnect (struct server_flow *flow) +{ + ASSERT(flow->connected) + ASSERT(flow != dying_server_flow) + + // disconnect input + PacketRecvConnector_DisconnectInput(&flow->connector); + + // set not connected + flow->connected = 0; +} diff --git a/external/badvpn_dns/client/client.h b/external/badvpn_dns/client/client.h new file mode 100644 index 00000000..595ed593 --- /dev/null +++ b/external/badvpn_dns/client/client.h @@ -0,0 +1,193 @@ +/** + * @file client.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOTE: all time values are in milliseconds + +// name of the program +#define PROGRAM_NAME "client" + +// server output buffer size +#define SERVER_BUFFER_MIN_PACKETS 200 + +// maximum UDP payload size +#define CLIENT_UDP_MTU 1472 + +// maximum number of pending TCP PasswordListener clients +#define TCP_MAX_PASSWORD_LISTENER_CLIENTS 50 + +// maximum number of peers +#define DEFAULT_MAX_PEERS 256 +// maximum number of peer's MAC addresses to remember +#define PEER_DEFAULT_MAX_MACS 16 +// maximum number of multicast addresses per peer +#define PEER_DEFAULT_MAX_GROUPS 16 +// how long we wait for a packet to reach full size before sending it (see FragmentProtoDisassembler latency argument) +#define PEER_DEFAULT_UDP_FRAGMENTATION_LATENCY 0 +// value related to how much out-of-order input we tolerate (see FragmentProtoAssembler num_frames argument) +#define PEER_UDP_ASSEMBLER_NUM_FRAMES 4 +// socket send buffer (SO_SNDBUF) for peer TCP connections, <=0 to not set +#define PEER_DEFAULT_TCP_SOCKET_SNDBUF 1048576 +// keep-alive packet interval for p2p communication +#define PEER_KEEPALIVE_INTERVAL 10000 +// keep-alive receive timer for p2p communication (after how long to consider the link down) +#define PEER_KEEPALIVE_RECEIVE_TIMER 22000 +// size of frame send buffer, in number of frames +#define PEER_DEFAULT_SEND_BUFFER_SIZE 32 +// size of frame send buffer for relayed packets, in number of frames +#define PEER_DEFAULT_SEND_BUFFER_RELAY_SIZE 32 +// time after an unused relay flow is freed (-1 for never) +#define PEER_RELAY_FLOW_INACTIVITY_TIME 10000 +// retry time +#define PEER_RETRY_TIME 5000 + +// for how long a peer can send no Membership Reports for a group +// before the peer and group are disassociated +#define DEFAULT_IGMP_GROUP_MEMBERSHIP_INTERVAL 260000 +// how long to wait for joins after a Group Specific query has been +// forwarded to a peer before assuming there are no listeners at the peer +#define DEFAULT_IGMP_LAST_MEMBER_QUERY_TIME 2000 + +// maximum bind addresses +#define MAX_BIND_ADDRS 8 +// maximum external addresses per bind address +#define MAX_EXT_ADDRS 8 +// maximum scopes +#define MAX_SCOPES 8 + +//#define SIMULATE_PEER_OUT_OF_BUFFER 70 + +struct server_flow { + PacketPassFairQueueFlow qflow; + SinglePacketBuffer encoder_buffer; + PacketRecvConnector connector; + int connected; +}; + +struct peer_data { + // peer identifier + peerid_t id; + + // flags provided by the server + int flags; + + // certificate reported by the server, defined only if using SSL + uint8_t cert[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_len; + char *common_name; + + // init job + BPending job_init; + + // server flow + struct server_flow *server_flow; + + // chat + int have_chat; + PeerChat chat; + int chat_send_msg_len; + + // resetpeer source (when chat fails) + int have_resetpeer; + uint8_t resetpeer_packet[sizeof(struct packetproto_header) + sizeof(struct sc_header) + sizeof(struct sc_client_resetpeer)]; + SinglePacketSource resetpeer_source; + + // local flow + DataProtoFlow local_dpflow; + + // frame decider peer + FrameDeciderPeer decider_peer; + + // receive peer + DPReceivePeer receive_peer; + + // flag if link objects are initialized + int have_link; + + // receive receiver + DPReceiveReceiver receive_receiver; + + // transport-specific link objects + union { + struct { + DatagramPeerIO pio; + uint16_t sendseed_nextid; + int sendseed_sent; + uint16_t sendseed_sent_id; + uint8_t sendseed_sent_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t sendseed_sent_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + uint16_t pending_recvseed_id; + BPending job_send_seed; + } udp; + struct { + StreamPeerIO pio; + } tcp; + } pio; + + // link sending + DataProtoSink send_dp; + + // relaying objects + struct peer_data *relaying_peer; // peer through which we are relaying, or NULL + LinkedList1Node relaying_list_node; // node in relay peer's relay_users + + // waiting for relay data + int waiting_relay; + LinkedList1Node waiting_relay_list_node; + + // retry timer + BTimer reset_timer; + + // relay server specific + int is_relay; + LinkedList1Node relay_list_node; + LinkedList1 relay_users; + + // binding state + int binding; + int binding_addrpos; + + // peers linked list node + LinkedList1Node list_node; +}; diff --git a/external/badvpn_dns/cmake/modules/COPYING-CMAKE-SCRIPTS b/external/badvpn_dns/cmake/modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 00000000..4b417765 --- /dev/null +++ b/external/badvpn_dns/cmake/modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/badvpn_dns/cmake/modules/FindGLIB2.cmake b/external/badvpn_dns/cmake/modules/FindGLIB2.cmake new file mode 100644 index 00000000..09fd98d8 --- /dev/null +++ b/external/badvpn_dns/cmake/modules/FindGLIB2.cmake @@ -0,0 +1,52 @@ +# - Try to find the GLIB2 libraries +# Once done this will define +# +# GLIB2_FOUND - system has glib2 +# GLIB2_INCLUDE_DIR - the glib2 include directory +# GLIB2_LIBRARIES - glib2 library + +# Copyright (c) 2008 Laurent Montel, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) + # Already in cache, be silent + set(GLIB2_FIND_QUIETLY TRUE) +endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES) + +find_package(PkgConfig) +pkg_check_modules(PC_LibGLIB2 QUIET glib-2.0) + +find_path(GLIB2_MAIN_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_LibGLIB2_INCLUDEDIR} + PATH_SUFFIXES glib-2.0) + +find_library(GLIB2_LIBRARY + NAMES glib-2.0 + HINTS ${PC_LibGLIB2_LIBDIR} +) + +set(GLIB2_LIBRARIES ${GLIB2_LIBRARY}) + +# search the glibconfig.h include dir under the same root where the library is found +get_filename_component(glib2LibDir "${GLIB2_LIBRARIES}" PATH) + +find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h + PATH_SUFFIXES glib-2.0/include + HINTS ${PC_LibGLIB2_INCLUDEDIR} "${glib2LibDir}" ${CMAKE_SYSTEM_LIBRARY_PATH}) + +set(GLIB2_INCLUDE_DIR "${GLIB2_MAIN_INCLUDE_DIR}") + +# not sure if this include dir is optional or required +# for now it is optional +if(GLIB2_INTERNAL_INCLUDE_DIR) + set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} "${GLIB2_INTERNAL_INCLUDE_DIR}") +endif(GLIB2_INTERNAL_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLIB2 DEFAULT_MSG GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR) + +mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES) diff --git a/external/badvpn_dns/cmake/modules/FindLibraryWithDebug.cmake b/external/badvpn_dns/cmake/modules/FindLibraryWithDebug.cmake new file mode 100644 index 00000000..58cd7308 --- /dev/null +++ b/external/badvpn_dns/cmake/modules/FindLibraryWithDebug.cmake @@ -0,0 +1,113 @@ +# +# FIND_LIBRARY_WITH_DEBUG +# -> enhanced FIND_LIBRARY to allow the search for an +# optional debug library with a WIN32_DEBUG_POSTFIX similar +# to CMAKE_DEBUG_POSTFIX when creating a shared lib +# it has to be the second and third argument + +# Copyright (c) 2007, Christian Ehrlicher, +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +MACRO(FIND_LIBRARY_WITH_DEBUG var_name win32_dbg_postfix_name dgb_postfix libname) + + IF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + # no WIN32_DEBUG_POSTFIX -> simply pass all arguments to FIND_LIBRARY + FIND_LIBRARY(${var_name} + ${win32_dbg_postfix_name} + ${dgb_postfix} + ${libname} + ${ARGN} + ) + + ELSE(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + IF(NOT WIN32) + # on non-win32 we don't need to take care about WIN32_DEBUG_POSTFIX + + FIND_LIBRARY(${var_name} ${libname} ${ARGN}) + + ELSE(NOT WIN32) + + # 1. get all possible libnames + SET(args ${ARGN}) + SET(newargs "") + SET(libnames_release "") + SET(libnames_debug "") + + LIST(LENGTH args listCount) + + IF("${libname}" STREQUAL "NAMES") + SET(append_rest 0) + LIST(APPEND args " ") + + FOREACH(i RANGE ${listCount}) + LIST(GET args ${i} val) + + IF(append_rest) + LIST(APPEND newargs ${val}) + ELSE(append_rest) + IF("${val}" STREQUAL "PATHS") + LIST(APPEND newargs ${val}) + SET(append_rest 1) + ELSE("${val}" STREQUAL "PATHS") + LIST(APPEND libnames_release "${val}") + LIST(APPEND libnames_debug "${val}${dgb_postfix}") + ENDIF("${val}" STREQUAL "PATHS") + ENDIF(append_rest) + + ENDFOREACH(i) + + ELSE("${libname}" STREQUAL "NAMES") + + # just one name + LIST(APPEND libnames_release "${libname}") + LIST(APPEND libnames_debug "${libname}${dgb_postfix}") + + SET(newargs ${args}) + + ENDIF("${libname}" STREQUAL "NAMES") + + # search the release lib + FIND_LIBRARY(${var_name}_RELEASE + NAMES ${libnames_release} + ${newargs} + ) + + # search the debug lib + FIND_LIBRARY(${var_name}_DEBUG + NAMES ${libnames_debug} + ${newargs} + ) + + IF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + # both libs found + SET(${var_name} optimized ${${var_name}_RELEASE} + debug ${${var_name}_DEBUG}) + + ELSE(${var_name}_RELEASE AND ${var_name}_DEBUG) + + IF(${var_name}_RELEASE) + + # only release found + SET(${var_name} ${${var_name}_RELEASE}) + + ELSE(${var_name}_RELEASE) + + # only debug (or nothing) found + SET(${var_name} ${${var_name}_DEBUG}) + + ENDIF(${var_name}_RELEASE) + + ENDIF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + MARK_AS_ADVANCED(${var_name}_RELEASE) + MARK_AS_ADVANCED(${var_name}_DEBUG) + + ENDIF(NOT WIN32) + + ENDIF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + +ENDMACRO(FIND_LIBRARY_WITH_DEBUG) diff --git a/external/badvpn_dns/cmake/modules/FindNSPR.cmake b/external/badvpn_dns/cmake/modules/FindNSPR.cmake new file mode 100644 index 00000000..6e8fed92 --- /dev/null +++ b/external/badvpn_dns/cmake/modules/FindNSPR.cmake @@ -0,0 +1,57 @@ +# - Try to find the NSPR library +# Once done this will define +# +# NSPR_FOUND - system has the NSPR library +# NSPR_INCLUDE_DIRS - Include paths needed +# NSPR_LIBRARY_DIRS - Linker paths needed +# NSPR_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (NSPR_LIBRARIES) + set(NSPR_FIND_QUIETLY TRUE) +endif () + +set(NSPR_FOUND FALSE) + +if (WIN32) + find_path(NSPR_FIND_INCLUDE_DIR prerror.h) + + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLDS WIN32_DEBUG_POSTFIX d NAMES plds4 libplds4) + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_PLC WIN32_DEBUG_POSTFIX d NAMES plc4 libplc4) + FIND_LIBRARY_WITH_DEBUG(NSPR_FIND_LIBRARIES_NSPR WIN32_DEBUG_POSTFIX d NAMES nspr4 libnspr4) + + if (NSPR_FIND_INCLUDE_DIR AND NSPR_FIND_LIBRARIES_PLDS AND NSPR_FIND_LIBRARIES_PLC AND NSPR_FIND_LIBRARIES_NSPR) + set(NSPR_FOUND TRUE) + set(NSPR_INCLUDE_DIRS "${NSPR_FIND_INCLUDE_DIR}" CACHE STRING "NSPR include dirs") + set(NSPR_LIBRARY_DIRS "" CACHE STRING "NSPR library dirs") + set(NSPR_LIBRARIES "${NSPR_FIND_LIBRARIES_PLDS};${NSPR_FIND_LIBRARIES_PLC};${NSPR_FIND_LIBRARIES_NSPR}" CACHE STRING "NSPR libraries") + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(NSPR_PC nspr) + + if (NSPR_PC_FOUND) + set(NSPR_FOUND TRUE) + set(NSPR_INCLUDE_DIRS "${NSPR_PC_INCLUDE_DIRS}" CACHE STRING "NSPR include dirs") + set(NSPR_LIBRARY_DIRS "${NSPR_PC_LIBRARY_DIRS}" CACHE STRING "NSPR library dirs") + set(NSPR_LIBRARIES "${NSPR_PC_LIBRARIES}" CACHE STRING "NSPR libraries") + endif () +endif () + +if (NSPR_FOUND) + if (NOT NSPR_FIND_QUIETLY) + MESSAGE(STATUS "Found NSPR: ${NSPR_INCLUDE_DIRS} ${NSPR_LIBRARY_DIRS} ${NSPR_LIBRARIES}") + endif () +else () + if (NSPR_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find NSPR") + endif () +endif () + +mark_as_advanced(NSPR_INCLUDE_DIRS NSPR_LIBRARY_DIRS NSPR_LIBRARIES) diff --git a/external/badvpn_dns/cmake/modules/FindNSS.cmake b/external/badvpn_dns/cmake/modules/FindNSS.cmake new file mode 100644 index 00000000..17fd45ab --- /dev/null +++ b/external/badvpn_dns/cmake/modules/FindNSS.cmake @@ -0,0 +1,57 @@ +# - Try to find the NSS library +# Once done this will define +# +# NSS_FOUND - system has the NSS library +# NSS_INCLUDE_DIRS - Include paths needed +# NSS_LIBRARY_DIRS - Linker paths needed +# NSS_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (NSS_LIBRARIES) + set(NSS_FIND_QUIETLY TRUE) +endif () + +set(NSS_FOUND FALSE) + +if (WIN32) + find_path(NSS_FIND_INCLUDE_DIR nss.h) + + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl3) + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_SMIME WIN32_DEBUG_POSTFIX d NAMES smime3) + FIND_LIBRARY_WITH_DEBUG(NSS_FIND_LIBRARIES_NSS WIN32_DEBUG_POSTFIX d NAMES nss3) + + if (NSS_FIND_INCLUDE_DIR AND NSS_FIND_LIBRARIES_SSL AND NSS_FIND_LIBRARIES_SMIME AND NSS_FIND_LIBRARIES_NSS) + set(NSS_FOUND TRUE) + set(NSS_INCLUDE_DIRS "${NSS_FIND_INCLUDE_DIR}" CACHE STRING "NSS include dirs") + set(NSS_LIBRARY_DIRS "" CACHE STRING "NSS library dirs") + set(NSS_LIBRARIES "${NSS_FIND_LIBRARIES_SSL};${NSS_FIND_LIBRARIES_SMIME};${NSS_FIND_LIBRARIES_NSS}" CACHE STRING "NSS libraries") + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(NSS_PC nss) + + if (NSS_PC_FOUND) + set(NSS_FOUND TRUE) + set(NSS_INCLUDE_DIRS "${NSS_PC_INCLUDE_DIRS}" CACHE STRING "NSS include dirs") + set(NSS_LIBRARY_DIRS "${NSS_PC_LIBRARY_DIRS}" CACHE STRING "NSS library dirs") + set(NSS_LIBRARIES "${NSS_PC_LIBRARIES}" CACHE STRING "NSS libraries") + endif () +endif () + +if (NSS_FOUND) + if (NOT NSS_FIND_QUIETLY) + MESSAGE(STATUS "Found NSS: ${NSS_INCLUDE_DIRS} ${NSS_LIBRARY_DIRS} ${NSS_LIBRARIES}") + endif () +else () + if (NSS_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find NSS") + endif () +endif () + +mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARY_DIRS NSS_LIBRARIES) diff --git a/external/badvpn_dns/cmake/modules/FindOpenSSL.cmake b/external/badvpn_dns/cmake/modules/FindOpenSSL.cmake new file mode 100644 index 00000000..4434e95b --- /dev/null +++ b/external/badvpn_dns/cmake/modules/FindOpenSSL.cmake @@ -0,0 +1,72 @@ +# - Try to find the OpenSSL library +# Once done this will define +# +# OpenSSL_FOUND - system has the OpenSSL library +# OpenSSL_INCLUDE_DIRS - Include paths needed +# OpenSSL_LIBRARY_DIRS - Linker paths needed +# OpenSSL_LIBRARIES - Libraries needed + +# Copyright (c) 2010, Ambroz Bizjak, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindLibraryWithDebug) + +if (OpenSSL_LIBRARIES) + set(OpenSSL_FIND_QUIETLY TRUE) +endif () + +set(OpenSSL_FOUND FALSE) + +if (WIN32) + find_path(OpenSSL_FIND_INCLUDE_DIR openssl/ssl.h) + + if (OpenSSL_FIND_INCLUDE_DIR) + # look for libraries built with GCC + find_library(OpenSSL_FIND_LIBRARIES_SSL NAMES ssl) + find_library(OpenSSL_FIND_LIBRARIES_CRYPTO NAMES crypto) + + if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_CRYPTO) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_CRYPTO}" CACHE STRING "OpenSSL libraries") + else () + # look for libraries built with MSVC + FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_SSL WIN32_DEBUG_POSTFIX d NAMES ssl ssleay ssleay32 libssleay32 ssleay32MD) + FIND_LIBRARY_WITH_DEBUG(OpenSSL_FIND_LIBRARIES_EAY WIN32_DEBUG_POSTFIX d NAMES eay libeay libeay32 libeay32MD) + + if (OpenSSL_FIND_LIBRARIES_SSL AND OpenSSL_FIND_LIBRARIES_EAY) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_LIBRARY_DIRS "" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_FIND_LIBRARIES_SSL};${OpenSSL_FIND_LIBRARIES_EAY}" CACHE STRING "OpenSSL libraries") + endif () + endif () + + if (OpenSSL_FOUND) + set(OpenSSL_INCLUDE_DIRS "${OpenSSL_FIND_INCLUDE_DIR}" CACHE STRING "OpenSSL include dirs") + endif () + endif () +else () + find_package(PkgConfig REQUIRED) + pkg_check_modules(OpenSSL_PC openssl) + + if (OpenSSL_PC_FOUND) + set(OpenSSL_FOUND TRUE) + set(OpenSSL_INCLUDE_DIRS "${OpenSSL_PC_INCLUDE_DIRS}" CACHE STRING "OpenSSL include dirs") + set(OpenSSL_LIBRARY_DIRS "${OpenSSL_PC_LIBRARY_DIRS}" CACHE STRING "OpenSSL library dirs") + set(OpenSSL_LIBRARIES "${OpenSSL_PC_LIBRARIES}" CACHE STRING "OpenSSL libraries") + endif () +endif () + +if (OpenSSL_FOUND) + if (NOT OpenSSL_FIND_QUIETLY) + MESSAGE(STATUS "Found OpenSSL: ${OpenSSL_INCLUDE_DIRS} ${OpenSSL_LIBRARY_DIRS} ${OpenSSL_LIBRARIES}") + endif () +else () + if (OpenSSL_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find OpenSSL") + endif () +endif () + +mark_as_advanced(OpenSSL_INCLUDE_DIRS OpenSSL_LIBRARY_DIRS OpenSSL_LIBRARIES) diff --git a/external/badvpn_dns/compile-tun2sock.sh b/external/badvpn_dns/compile-tun2sock.sh new file mode 100755 index 00000000..fbc23887 --- /dev/null +++ b/external/badvpn_dns/compile-tun2sock.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# +# Compiles tun2socks for Linux. +# Intended as a convenience if you don't want to deal with CMake. + +# Input environment vars: +# SRCDIR - BadVPN source code +# CC - compiler +# CFLAGS - compiler compile flags +# LDFLAGS - compiler link flags +# ENDIAN - "little" or "big" +# KERNEL - "2.6" or "2.4", default "2.6" +# +# Puts object files and the executable in the working directory. +# + +if [[ -z $SRCDIR ]] || [[ ! -e $SRCDIR/CMakeLists.txt ]]; then + echo "SRCDIR is wrong" + exit 1 +fi + +if ! "${CC}" --version &>/dev/null; then + echo "CC is wrong" + exit 1 +fi + +if [[ $ENDIAN != "little" ]] && [[ $ENDIAN != "big" ]]; then + echo "ENDIAN is wrong" + exit 1 +fi + +if [[ -z $KERNEL ]]; then + KERNEL="2.6" +elif [[ $KERNEL != "2.6" ]] && [[ $KERNEL != "2.4" ]]; then + echo "KERNEL is wrong" + exit 1 +fi + +CFLAGS="${CFLAGS} -std=gnu99" +INCLUDES=( "-I${SRCDIR}" "-I${SRCDIR}/lwip/src/include/ipv4" "-I${SRCDIR}/lwip/src/include/ipv6" "-I${SRCDIR}/lwip/src/include" "-I${SRCDIR}/lwip/custom" ) +DEFS=( -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE ) + +[[ $KERNEL = "2.4" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_USE_SELFPIPE -DBADVPN_USE_POLL ) || DEFS=( "${DEFS[@]}" -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL ) + +[[ $ENDIAN = "little" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_LITTLE_ENDIAN ) || DEFS=( "${DEFS[@]}" -DBADVPN_BIG_ENDIAN ) + +SOURCES=" +base/BLog_syslog.c +system/BReactor_badvpn.c +system/BSignal.c +system/BConnection_unix.c +system/BTime.c +system/BUnixSignal.c +system/BNetwork.c +flow/StreamRecvInterface.c +flow/PacketRecvInterface.c +flow/PacketPassInterface.c +flow/StreamPassInterface.c +flow/SinglePacketBuffer.c +flow/BufferWriter.c +flow/PacketBuffer.c +flow/PacketStreamSender.c +flow/PacketPassConnector.c +flow/PacketProtoFlow.c +flow/PacketPassFairQueue.c +flow/PacketProtoEncoder.c +flow/PacketProtoDecoder.c +socksclient/BSocksClient.c +tuntap/BTap.c +lwip/src/core/timers.c +lwip/src/core/udp.c +lwip/src/core/memp.c +lwip/src/core/init.c +lwip/src/core/pbuf.c +lwip/src/core/tcp.c +lwip/src/core/tcp_out.c +lwip/src/core/netif.c +lwip/src/core/def.c +lwip/src/core/mem.c +lwip/src/core/tcp_in.c +lwip/src/core/stats.c +lwip/src/core/inet_chksum.c +lwip/src/core/ipv4/icmp.c +lwip/src/core/ipv4/ip4.c +lwip/src/core/ipv4/ip4_addr.c +lwip/src/core/ipv4/ip_frag.c +lwip/src/core/ipv6/ip6.c +lwip/src/core/ipv6/nd6.c +lwip/src/core/ipv6/icmp6.c +lwip/src/core/ipv6/ip6_addr.c +lwip/src/core/ipv6/ip6_frag.c +lwip/custom/sys.c +tun2socks/tun2socks.c +base/DebugObject.c +base/BLog.c +base/BPending.c +flowextra/PacketPassInactivityMonitor.c +tun2socks/SocksUdpGwClient.c +udpgw_client/UdpGwClient.c +" + +set -e +set -x + +OBJS=() +for f in $SOURCES; do + obj=$(basename "${f}").o + "${CC}" -c ${CFLAGS} "${INCLUDES[@]}" "${DEFS[@]}" "${SRCDIR}/${f}" -o "${obj}" + OBJS=( "${OBJS[@]}" "${obj}" ) +done + +"${CC}" ${LDFLAGS} "${OBJS[@]}" -o tun2socks -lrt diff --git a/external/badvpn_dns/compile-udpgw.sh b/external/badvpn_dns/compile-udpgw.sh new file mode 100755 index 00000000..5f132f9f --- /dev/null +++ b/external/badvpn_dns/compile-udpgw.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# Compiles udpgw for Linux. +# Intended as a convenience if you don't want to deal with CMake. + +# Input environment vars: +# SRCDIR - BadVPN source code +# CC - compiler +# CFLAGS - compiler compile flags +# LDFLAGS - compiler link flags +# ENDIAN - "little" or "big" +# KERNEL - "2.6" or "2.4", default "2.6" +# +# Puts object files and the executable in the working directory. +# + +if [[ -z $SRCDIR ]] || [[ ! -e $SRCDIR/CMakeLists.txt ]]; then + echo "SRCDIR is wrong" + exit 1 +fi + +if ! "${CC}" --version &>/dev/null; then + echo "CC is wrong" + exit 1 +fi + +if [[ $ENDIAN != "little" ]] && [[ $ENDIAN != "big" ]]; then + echo "ENDIAN is wrong" + exit 1 +fi + +if [[ -z $KERNEL ]]; then + KERNEL="2.6" +elif [[ $KERNEL != "2.6" ]] && [[ $KERNEL != "2.4" ]]; then + echo "KERNEL is wrong" + exit 1 +fi + +CFLAGS="${CFLAGS} -std=gnu99" +INCLUDES=( "-I${SRCDIR}" ) +DEFS=( -DBADVPN_THREAD_SAFE=0 -DBADVPN_LINUX -DBADVPN_BREACTOR_BADVPN -D_GNU_SOURCE ) + +[[ $KERNEL = "2.4" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_USE_SELFPIPE -DBADVPN_USE_POLL ) || DEFS=( "${DEFS[@]}" -DBADVPN_USE_SIGNALFD -DBADVPN_USE_EPOLL ) + +[[ $ENDIAN = "little" ]] && DEFS=( "${DEFS[@]}" -DBADVPN_LITTLE_ENDIAN ) || DEFS=( "${DEFS[@]}" -DBADVPN_BIG_ENDIAN ) + +SOURCES=" +base/BLog_syslog.c +system/BReactor_badvpn.c +system/BSignal.c +system/BConnection_unix.c +system/BDatagram_unix.c +system/BTime.c +system/BUnixSignal.c +system/BNetwork.c +flow/StreamRecvInterface.c +flow/PacketRecvInterface.c +flow/PacketPassInterface.c +flow/StreamPassInterface.c +flow/SinglePacketBuffer.c +flow/BufferWriter.c +flow/PacketBuffer.c +flow/PacketStreamSender.c +flow/PacketProtoFlow.c +flow/PacketPassFairQueue.c +flow/PacketProtoEncoder.c +flow/PacketProtoDecoder.c +base/DebugObject.c +base/BLog.c +base/BPending.c +udpgw/udpgw.c +" + +set -e +set -x + +OBJS=() +for f in $SOURCES; do + obj=$(basename "${f}").o + "${CC}" -c ${CFLAGS} "${INCLUDES[@]}" "${DEFS[@]}" "${SRCDIR}/${f}" -o "${obj}" + OBJS=( "${OBJS[@]}" "${obj}" ) +done + +"${CC}" ${LDFLAGS} "${OBJS[@]}" -o udpgw -lrt diff --git a/external/badvpn_dns/dhcpclient/BDHCPClient.c b/external/badvpn_dns/dhcpclient/BDHCPClient.c new file mode 100644 index 00000000..70ee6adf --- /dev/null +++ b/external/badvpn_dns/dhcpclient/BDHCPClient.c @@ -0,0 +1,340 @@ +/** + * @file BDHCPClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_OVERHEAD (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static const struct sock_filter dhcp_sock_filter[] = { + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 9), // A <- IP protocol + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPV4_PROTOCOL_UDP, 0, 3), // IP protocol = UDP ? + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22), // A <- UDP destination port + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1), // UDP destination port = DHCP client ? + BPF_STMT(BPF_RET + BPF_K, 65535), // return all + BPF_STMT(BPF_RET + BPF_K, 0) // ignore +}; + +static void dgram_handler (BDHCPClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "packet socket error"); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDHCPCLIENT_EVENT_ERROR)); + return; +} + +static void dhcp_handler (BDHCPClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + + switch (event) { + case BDHCPCLIENTCORE_EVENT_UP: + ASSERT(!o->up) + o->up = 1; + o->handler(o->user, BDHCPCLIENT_EVENT_UP); + return; + + case BDHCPCLIENTCORE_EVENT_DOWN: + ASSERT(o->up) + o->up = 0; + o->handler(o->user, BDHCPCLIENT_EVENT_DOWN); + return; + + default: + ASSERT(0); + } +} + +static void dhcp_func_getsendermac (BDHCPClient *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + + BAddr remote_addr; + BIPAddr local_addr; + if (!BDatagram_GetLastReceiveAddrs(&o->dgram, &remote_addr, &local_addr)) { + BLog(BLOG_ERROR, "BDatagram_GetLastReceiveAddrs failed"); + goto fail; + } + + if (remote_addr.type != BADDR_TYPE_PACKET) { + BLog(BLOG_ERROR, "address type invalid"); + goto fail; + } + + if (remote_addr.packet.header_type != BADDR_PACKET_HEADER_TYPE_ETHERNET) { + BLog(BLOG_ERROR, "address header type invalid"); + goto fail; + } + + memcpy(out_mac, remote_addr.packet.phys_addr, 6); + return; + +fail: + memset(out_mac, 0, 6); +} + +int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // get interface information + uint8_t if_mac[6]; + int if_mtu; + int if_index; + if (!badvpn_get_iface_info(ifname, if_mac, &if_mtu, &if_index)) { + BLog(BLOG_ERROR, "failed to get interface information"); + goto fail0; + } + + BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d", + if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index); + + if (if_mtu < IPUDP_OVERHEAD) { + BLog(BLOG_ERROR, "MTU is too small for UDP/IP !?!"); + goto fail0; + } + + int dhcp_mtu = if_mtu - IPUDP_OVERHEAD; + + // init dgram + if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) { + BLog(BLOG_ERROR, "BDatagram_Init failed"); + goto fail0; + } + + // set socket filter + { + struct sock_filter filter[sizeof(dhcp_sock_filter) / sizeof(dhcp_sock_filter[0])]; + memcpy(filter, dhcp_sock_filter, sizeof(filter)); + struct sock_fprog fprog = { + .len = sizeof(filter) / sizeof(filter[0]), + .filter = filter + }; + if (setsockopt(BDatagram_GetFd(&o->dgram), SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) { + BLog(BLOG_NOTICE, "not using socket filter"); + } + } + + // bind dgram + BAddr bind_addr; + BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac); + if (!BDatagram_Bind(&o->dgram, bind_addr)) { + BLog(BLOG_ERROR, "BDatagram_Bind failed"); + goto fail1; + } + + // set dgram send addresses + BAddr dest_addr; + uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_IPV4), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac); + BIPAddr local_addr; + BIPAddr_InitInvalid(&local_addr); + BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr); + + // init dgram interfaces + BDatagram_SendAsync_Init(&o->dgram, if_mtu); + BDatagram_RecvAsync_Init(&o->dgram, if_mtu); + + // init sending + + // init copier + PacketCopier_Init(&o->send_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor)); + + // init encoder + DHCPIpUdpEncoder_Init(&o->send_encoder, PacketCopier_GetOutput(&o->send_copier), BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->send_buffer, DHCPIpUdpEncoder_GetOutput(&o->send_encoder), BDatagram_SendAsync_GetIf(&o->dgram), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail2; + } + + // init receiving + + // init copier + PacketCopier_Init(&o->recv_copier, dhcp_mtu, BReactor_PendingGroup(o->reactor)); + + // init decoder + DHCPIpUdpDecoder_Init(&o->recv_decoder, PacketCopier_GetInput(&o->recv_copier), BReactor_PendingGroup(o->reactor)); + + // init buffer + if (!SinglePacketBuffer_Init(&o->recv_buffer, BDatagram_RecvAsync_GetIf(&o->dgram), DHCPIpUdpDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail3; + } + + // init options + struct BDHCPClientCore_opts core_opts; + core_opts.hostname = opts.hostname; + core_opts.vendorclassid = opts.vendorclassid; + core_opts.clientid = opts.clientid; + core_opts.clientid_len = opts.clientid_len; + + // auto-generate clientid from MAC if requested + uint8_t mac_cid[7]; + if (opts.auto_clientid) { + mac_cid[0] = DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET; + memcpy(mac_cid + 1, if_mac, 6); + core_opts.clientid = mac_cid; + core_opts.clientid_len = sizeof(mac_cid); + } + + // init dhcp + if (!BDHCPClientCore_Init(&o->dhcp, PacketCopier_GetInput(&o->send_copier), PacketCopier_GetOutput(&o->recv_copier), if_mac, core_opts, o->reactor, random2, o, + (BDHCPClientCore_func_getsendermac)dhcp_func_getsendermac, + (BDHCPClientCore_handler)dhcp_handler + )) { + BLog(BLOG_ERROR, "BDHCPClientCore_Init failed"); + goto fail4; + } + + // set not up + o->up = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail4: + SinglePacketBuffer_Free(&o->recv_buffer); +fail3: + DHCPIpUdpDecoder_Free(&o->recv_decoder); + PacketCopier_Free(&o->recv_copier); + SinglePacketBuffer_Free(&o->send_buffer); +fail2: + DHCPIpUdpEncoder_Free(&o->send_encoder); + PacketCopier_Free(&o->send_copier); + BDatagram_RecvAsync_Free(&o->dgram); + BDatagram_SendAsync_Free(&o->dgram); +fail1: + BDatagram_Free(&o->dgram); +fail0: + return 0; +} + +void BDHCPClient_Free (BDHCPClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free dhcp + BDHCPClientCore_Free(&o->dhcp); + + // free receiving + SinglePacketBuffer_Free(&o->recv_buffer); + DHCPIpUdpDecoder_Free(&o->recv_decoder); + PacketCopier_Free(&o->recv_copier); + + // free sending + SinglePacketBuffer_Free(&o->send_buffer); + DHCPIpUdpEncoder_Free(&o->send_encoder); + PacketCopier_Free(&o->send_copier); + + // free dgram interfaces + BDatagram_RecvAsync_Free(&o->dgram); + BDatagram_SendAsync_Free(&o->dgram); + + // free dgram + BDatagram_Free(&o->dgram); +} + +int BDHCPClient_IsUp (BDHCPClient *o) +{ + DebugObject_Access(&o->d_obj); + + return o->up; +} + +void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetClientIP(&o->dhcp, out_ip); +} + +void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetClientMask(&o->dhcp, out_mask); +} + +int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return BDHCPClientCore_GetRouter(&o->dhcp, out_router); +} + +int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return BDHCPClientCore_GetDNS(&o->dhcp, out_dns_servers, max_dns_servers); +} + +void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + BDHCPClientCore_GetServerMAC(&o->dhcp, out_mac); +} diff --git a/external/badvpn_dns/dhcpclient/BDHCPClient.h b/external/badvpn_dns/dhcpclient/BDHCPClient.h new file mode 100644 index 00000000..c0da0c41 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/BDHCPClient.h @@ -0,0 +1,87 @@ +/** + * @file BDHCPClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client. + */ + +#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENT_H +#define BADVPN_DHCPCLIENT_BDHCPCLIENT_H + +#include +#include +#include +#include +#include +#include +#include + +#define BDHCPCLIENT_EVENT_UP 1 +#define BDHCPCLIENT_EVENT_DOWN 2 +#define BDHCPCLIENT_EVENT_ERROR 3 + +#define BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS + +typedef void (*BDHCPClient_handler) (void *user, int event); + +typedef struct { + BReactor *reactor; + BDatagram dgram; + BDHCPClient_handler handler; + void *user; + PacketCopier send_copier; + DHCPIpUdpEncoder send_encoder; + SinglePacketBuffer send_buffer; + SinglePacketBuffer recv_buffer; + DHCPIpUdpDecoder recv_decoder; + PacketCopier recv_copier; + BDHCPClientCore dhcp; + int up; + DebugError d_err; + DebugObject d_obj; +} BDHCPClient; + +struct BDHCPClient_opts { + const char *hostname; + const char *vendorclassid; + const uint8_t *clientid; + size_t clientid_len; + int auto_clientid; +}; + +int BDHCPClient_Init (BDHCPClient *o, const char *ifname, struct BDHCPClient_opts opts, BReactor *reactor, BRandom2 *random2, BDHCPClient_handler handler, void *user); +void BDHCPClient_Free (BDHCPClient *o); +int BDHCPClient_IsUp (BDHCPClient *o); +void BDHCPClient_GetClientIP (BDHCPClient *o, uint32_t *out_ip); +void BDHCPClient_GetClientMask (BDHCPClient *o, uint32_t *out_mask); +int BDHCPClient_GetRouter (BDHCPClient *o, uint32_t *out_router); +int BDHCPClient_GetDNS (BDHCPClient *o, uint32_t *out_dns_servers, size_t max_dns_servers); +void BDHCPClient_GetServerMAC (BDHCPClient *o, uint8_t *out_mac); + +#endif diff --git a/external/badvpn_dns/dhcpclient/BDHCPClientCore.c b/external/badvpn_dns/dhcpclient/BDHCPClientCore.c new file mode 100644 index 00000000..5a605e42 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/BDHCPClientCore.c @@ -0,0 +1,860 @@ +/** + * @file BDHCPClientCore.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define RESET_TIMEOUT 4000 +#define REQUEST_TIMEOUT 3000 +#define RENEW_REQUEST_TIMEOUT 20000 +#define MAX_REQUESTS 4 +#define RENEW_TIMEOUT(lease) ((btime_t)500 * (lease)) +#define XID_REUSE_MAX 8 + +#define LEASE_TIMEOUT(lease) ((btime_t)1000 * (lease) - RENEW_TIMEOUT(lease)) + +#define STATE_RESETTING 1 +#define STATE_SENT_DISCOVER 2 +#define STATE_SENT_REQUEST 3 +#define STATE_FINISHED 4 +#define STATE_RENEWING 5 + +#define IP_UDP_HEADERS_SIZE 28 + +static void report_up (BDHCPClientCore *o) +{ + o->handler(o->user, BDHCPCLIENTCORE_EVENT_UP); + return; +} + +static void report_down (BDHCPClientCore *o) +{ + o->handler(o->user, BDHCPCLIENTCORE_EVENT_DOWN); + return; +} + +static void send_message ( + BDHCPClientCore *o, + int type, + uint32_t xid, + int have_requested_ip_address, uint32_t requested_ip_address, + int have_dhcp_server_identifier, uint32_t dhcp_server_identifier +) +{ + ASSERT(type == DHCP_MESSAGE_TYPE_DISCOVER || type == DHCP_MESSAGE_TYPE_REQUEST) + + if (o->sending) { + BLog(BLOG_ERROR, "already sending"); + return; + } + + // write header + struct dhcp_header header; + memset(&header, 0, sizeof(header)); + header.op = hton8(DHCP_OP_BOOTREQUEST); + header.htype = hton8(DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET); + header.hlen = hton8(6); + header.xid = xid; + header.secs = hton16(0); + memcpy(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr)); + header.magic = hton32(DHCP_MAGIC); + memcpy(o->send_buf, &header, sizeof(header)); + + // write options + + char *out = o->send_buf + sizeof(header); + struct dhcp_option_header oh; + + // DHCP message type + { + oh.type = hton8(DHCP_OPTION_DHCP_MESSAGE_TYPE); + oh.len = hton8(sizeof(struct dhcp_option_dhcp_message_type)); + struct dhcp_option_dhcp_message_type opt; + opt.type = hton8(type); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (have_requested_ip_address) { + // requested IP address + oh.type = hton8(DHCP_OPTION_REQUESTED_IP_ADDRESS); + oh.len = hton8(sizeof(struct dhcp_option_addr)); + struct dhcp_option_addr opt; + opt.addr = requested_ip_address; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (have_dhcp_server_identifier) { + // DHCP server identifier + oh.type = hton8(DHCP_OPTION_DHCP_SERVER_IDENTIFIER); + oh.len = hton8(sizeof(struct dhcp_option_dhcp_server_identifier)); + struct dhcp_option_dhcp_server_identifier opt; + opt.id = dhcp_server_identifier; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + // maximum message size + { + oh.type = hton8(DHCP_OPTION_MAXIMUM_MESSAGE_SIZE); + oh.len = hton8(sizeof(struct dhcp_option_maximum_message_size)); + struct dhcp_option_maximum_message_size opt; + opt.size = hton16(IP_UDP_HEADERS_SIZE + PacketRecvInterface_GetMTU(o->recv_if)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + // parameter request list + { + oh.type = hton8(DHCP_OPTION_PARAMETER_REQUEST_LIST); + oh.len = hton8(4); + uint8_t opt[4]; + opt[0] = DHCP_OPTION_SUBNET_MASK; + opt[1] = DHCP_OPTION_ROUTER; + opt[2] = DHCP_OPTION_DOMAIN_NAME_SERVER; + opt[3] = DHCP_OPTION_IP_ADDRESS_LEASE_TIME; + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), &opt, sizeof(opt)); + out += sizeof(oh) + sizeof(opt); + } + + if (o->hostname) { + // host name + oh.type = hton8(DHCP_OPTION_HOST_NAME); + oh.len = hton8(strlen(o->hostname)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->hostname, strlen(o->hostname)); + out += sizeof(oh) + strlen(o->hostname); + } + + if (o->vendorclassid) { + // vendor class identifier + oh.type = hton8(DHCP_OPTION_VENDOR_CLASS_IDENTIFIER); + oh.len = hton8(strlen(o->vendorclassid)); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->vendorclassid, strlen(o->vendorclassid)); + out += sizeof(oh) + strlen(o->vendorclassid); + } + + if (o->clientid) { + // client identifier + oh.type = hton8(DHCP_OPTION_CLIENT_IDENTIFIER); + oh.len = hton8(o->clientid_len); + memcpy(out, &oh, sizeof(oh)); + memcpy(out + sizeof(oh), o->clientid, o->clientid_len); + out += sizeof(oh) + o->clientid_len; + } + + // end option + uint8_t end = 0xFF; + memcpy(out, &end, sizeof(end)); + out += sizeof(end); + + // send it + PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)o->send_buf, out - o->send_buf); + o->sending = 1; +} + +static void send_handler_done (BDHCPClientCore *o) +{ + ASSERT(o->sending) + DebugObject_Access(&o->d_obj); + + o->sending = 0; +} + +static void recv_handler_done (BDHCPClientCore *o, int data_len) +{ + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + // receive more packets + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf); + + if (o->state == STATE_RESETTING) { + return; + } + + // check header + + if (data_len < sizeof(struct dhcp_header)) { + return; + } + + struct dhcp_header header; + memcpy(&header, o->recv_buf, sizeof(header)); + + if (ntoh8(header.op) != DHCP_OP_BOOTREPLY) { + return; + } + + if (ntoh8(header.htype) != DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET) { + return; + } + + if (ntoh8(header.hlen) != 6) { + return; + } + + if (header.xid != o->xid) { + return; + } + + if (memcmp(header.chaddr, o->client_mac_addr, sizeof(o->client_mac_addr))) { + return; + } + + if (ntoh32(header.magic) != DHCP_MAGIC) { + return; + } + + // parse and check options + + uint8_t *pos = (uint8_t *)o->recv_buf + sizeof(header); + int len = data_len - sizeof(header); + + int have_end = 0; + + int dhcp_message_type = -1; + + int have_dhcp_server_identifier = 0; + uint32_t dhcp_server_identifier = 0; // to remove warning + + int have_ip_address_lease_time = 0; + uint32_t ip_address_lease_time = 0; // to remove warning + + int have_subnet_mask = 0; + uint32_t subnet_mask = 0; // to remove warning + + int have_router = 0; + uint32_t router = 0; // to remove warning + + int domain_name_servers_count = 0; + uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS]; + + while (len > 0) { + // padding option ? + if (*pos == 0) { + pos++; + len--; + continue; + } + + if (have_end) { + return; + } + + // end option ? + if (*pos == 0xff) { + pos++; + len--; + have_end = 1; + continue; + } + + // check option header + if (len < sizeof(struct dhcp_option_header)) { + return; + } + struct dhcp_option_header opt; + memcpy(&opt, pos, sizeof(opt)); + pos += sizeof(opt); + len -= sizeof(opt); + int opt_type = ntoh8(opt.type); + int opt_len = ntoh8(opt.len); + + // check option payload + if (opt_len > len) { + return; + } + uint8_t *optval = pos; + pos += opt_len; + len -= opt_len; + + switch (opt_type) { + case DHCP_OPTION_DHCP_MESSAGE_TYPE: { + if (opt_len != sizeof(struct dhcp_option_dhcp_message_type)) { + return; + } + struct dhcp_option_dhcp_message_type val; + memcpy(&val, optval, sizeof(val)); + + dhcp_message_type = ntoh8(val.type); + } break; + + case DHCP_OPTION_DHCP_SERVER_IDENTIFIER: { + if (opt_len != sizeof(struct dhcp_option_dhcp_server_identifier)) { + return; + } + struct dhcp_option_dhcp_server_identifier val; + memcpy(&val, optval, sizeof(val)); + + dhcp_server_identifier = val.id; + have_dhcp_server_identifier = 1; + } break; + + case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: { + if (opt_len != sizeof(struct dhcp_option_time)) { + return; + } + struct dhcp_option_time val; + memcpy(&val, optval, sizeof(val)); + + ip_address_lease_time = ntoh32(val.time); + have_ip_address_lease_time = 1; + } break; + + case DHCP_OPTION_SUBNET_MASK: { + if (opt_len != sizeof(struct dhcp_option_addr)) { + return; + } + struct dhcp_option_addr val; + memcpy(&val, optval, sizeof(val)); + + subnet_mask = val.addr; + have_subnet_mask = 1; + } break; + + case DHCP_OPTION_ROUTER: { + if (opt_len != sizeof(struct dhcp_option_addr)) { + return; + } + struct dhcp_option_addr val; + memcpy(&val, optval, sizeof(val)); + + router = val.addr; + have_router = 1; + } break; + + case DHCP_OPTION_DOMAIN_NAME_SERVER: { + if (opt_len % sizeof(struct dhcp_option_addr)) { + return; + } + + int num_servers = opt_len / sizeof(struct dhcp_option_addr); + + int i; + for (i = 0; i < num_servers && i < BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS; i++) { + struct dhcp_option_addr addr; + memcpy(&addr, optval + i * sizeof(addr), sizeof(addr)); + domain_name_servers[i] = addr.addr; + } + + domain_name_servers_count = i; + } break; + } + } + + if (!have_end) { + return; + } + + if (dhcp_message_type == -1) { + return; + } + + if (dhcp_message_type != DHCP_MESSAGE_TYPE_OFFER && dhcp_message_type != DHCP_MESSAGE_TYPE_ACK && dhcp_message_type != DHCP_MESSAGE_TYPE_NAK) { + return; + } + + if (!have_dhcp_server_identifier) { + return; + } + + if (dhcp_message_type == DHCP_MESSAGE_TYPE_NAK) { + if (o->state != STATE_SENT_REQUEST && o->state != STATE_FINISHED && o->state != STATE_RENEWING) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + if (o->state == STATE_SENT_REQUEST) { + BLog(BLOG_INFO, "received NAK (in sent request)"); + + // stop request timer + BReactor_RemoveTimer(o->reactor, &o->request_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + } + else if (o->state == STATE_FINISHED) { + BLog(BLOG_INFO, "received NAK (in finished)"); + + // stop renew timer + BReactor_RemoveTimer(o->reactor, &o->renew_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + + // report to user + report_down(o); + return; + } + else { // STATE_RENEWING + BLog(BLOG_INFO, "received NAK (in renewing)"); + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // stop lease timer + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + + // start reset timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_RESETTING; + + // report to user + report_down(o); + return; + } + + return; + } + + if (ntoh32(header.yiaddr) == 0) { + return; + } + + if (!have_ip_address_lease_time) { + return; + } + + if (!have_subnet_mask) { + return; + } + + if (o->state == STATE_SENT_DISCOVER && dhcp_message_type == DHCP_MESSAGE_TYPE_OFFER) { + BLog(BLOG_INFO, "received OFFER"); + + // remember offer + o->offered.yiaddr = header.yiaddr; + o->offered.dhcp_server_identifier = dhcp_server_identifier; + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier); + + // stop reset timer + BReactor_RemoveTimer(o->reactor, &o->reset_timer); + + // start request timer + BReactor_SetTimer(o->reactor, &o->request_timer); + + // set state + o->state = STATE_SENT_REQUEST; + + // set request count + o->request_count = 1; + } + else if (o->state == STATE_SENT_REQUEST && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) { + if (header.yiaddr != o->offered.yiaddr) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + BLog(BLOG_INFO, "received ACK (in sent request)"); + + // remember stuff + o->acked.ip_address_lease_time = ip_address_lease_time; + o->acked.subnet_mask = subnet_mask; + o->acked.have_router = have_router; + if (have_router) { + o->acked.router = router; + } + o->acked.domain_name_servers_count = domain_name_servers_count; + memcpy(o->acked.domain_name_servers, domain_name_servers, domain_name_servers_count * sizeof(uint32_t)); + o->func_getsendermac(o->user, o->acked.server_mac); + + // stop request timer + BReactor_RemoveTimer(o->reactor, &o->request_timer); + + // start renew timer + BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_FINISHED; + + // report to user + report_up(o); + return; + } + else if (o->state == STATE_RENEWING && dhcp_message_type == DHCP_MESSAGE_TYPE_ACK) { + if (header.yiaddr != o->offered.yiaddr) { + return; + } + + if (dhcp_server_identifier != o->offered.dhcp_server_identifier) { + return; + } + + // TODO: check parameters? + + BLog(BLOG_INFO, "received ACK (in renewing)"); + + // remember stuff + o->acked.ip_address_lease_time = ip_address_lease_time; + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // stop lease timer + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + + // start renew timer + BReactor_SetTimerAfter(o->reactor, &o->renew_timer, RENEW_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_FINISHED; + } +} + +static void start_process (BDHCPClientCore *o, int force_new_xid) +{ + if (force_new_xid || o->xid_reuse_counter == XID_REUSE_MAX) { + // generate xid + if (!BRandom2_GenBytes(o->random2, &o->xid, sizeof(o->xid))) { + BLog(BLOG_ERROR, "BRandom2_GenBytes failed"); + o->xid = UINT32_C(3416960072); + } + + // reset counter + o->xid_reuse_counter = 0; + } + + // increment counter + o->xid_reuse_counter++; + + // send discover + send_message(o, DHCP_MESSAGE_TYPE_DISCOVER, o->xid, 0, 0, 0, 0); + + // set timer + BReactor_SetTimer(o->reactor, &o->reset_timer); + + // set state + o->state = STATE_SENT_DISCOVER; +} + +static void reset_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RESETTING || o->state == STATE_SENT_DISCOVER) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "reset timer"); + + start_process(o, (o->state == STATE_RESETTING)); +} + +static void request_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_SENT_REQUEST) + ASSERT(o->request_count >= 1) + ASSERT(o->request_count <= MAX_REQUESTS) + DebugObject_Access(&o->d_obj); + + // if we have sent enough requests, start again + if (o->request_count == MAX_REQUESTS) { + BLog(BLOG_INFO, "request timer, aborting"); + + start_process(o, 0); + return; + } + + BLog(BLOG_INFO, "request timer, retrying"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 1, o->offered.dhcp_server_identifier); + + // start request timer + BReactor_SetTimer(o->reactor, &o->request_timer); + + // increment request count + o->request_count++; +} + +static void renew_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_FINISHED) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "renew timer"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0); + + // start renew request timer + BReactor_SetTimer(o->reactor, &o->renew_request_timer); + + // start lease timer + BReactor_SetTimerAfter(o->reactor, &o->lease_timer, LEASE_TIMEOUT(o->acked.ip_address_lease_time)); + + // set state + o->state = STATE_RENEWING; +} + +static void renew_request_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "renew request timer"); + + // send request + send_message(o, DHCP_MESSAGE_TYPE_REQUEST, o->xid, 1, o->offered.yiaddr, 0, 0); + + // start renew request timer + BReactor_SetTimer(o->reactor, &o->renew_request_timer); +} + +static void lease_timer_handler (BDHCPClientCore *o) +{ + ASSERT(o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + BLog(BLOG_INFO, "lease timer"); + + // stop renew request timer + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + + // start again now + start_process(o, 1); + + // report to user + report_down(o); + return; +} + +static bsize_t maybe_len (const char *str) +{ + return bsize_fromsize(str ? strlen(str) : 0); +} + +int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, + uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, + BRandom2 *random2, void *user, + BDHCPClientCore_func_getsendermac func_getsendermac, + BDHCPClientCore_handler handler) +{ + ASSERT(PacketPassInterface_GetMTU(send_if) == PacketRecvInterface_GetMTU(recv_if)) + ASSERT(PacketPassInterface_GetMTU(send_if) >= 576 - IP_UDP_HEADERS_SIZE) + ASSERT(func_getsendermac) + ASSERT(handler) + + // init arguments + o->send_if = send_if; + o->recv_if = recv_if; + memcpy(o->client_mac_addr, client_mac_addr, sizeof(o->client_mac_addr)); + o->reactor = reactor; + o->random2 = random2; + o->user = user; + o->func_getsendermac = func_getsendermac; + o->handler = handler; + + o->hostname = NULL; + o->vendorclassid = NULL; + o->clientid = NULL; + o->clientid_len = 0; + + // copy options + if (opts.hostname && !(o->hostname = strdup(opts.hostname))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail0; + } + if (opts.vendorclassid && !(o->vendorclassid = strdup(opts.vendorclassid))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail0; + } + if (opts.clientid) { + if (!(o->clientid = BAlloc(opts.clientid_len))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + memcpy(o->clientid, opts.clientid, opts.clientid_len); + o->clientid_len = opts.clientid_len; + } + + // make sure options aren't too long + bsize_t opts_size = bsize_add(maybe_len(o->hostname), bsize_add(maybe_len(o->vendorclassid), bsize_fromsize(o->clientid_len))); + if (opts_size.is_overflow || opts_size.value > 100) { + BLog(BLOG_ERROR, "options too long together"); + goto fail0; + } + if (o->hostname && strlen(o->hostname) > 255) { + BLog(BLOG_ERROR, "hostname too long"); + goto fail0; + } + if (o->vendorclassid && strlen(o->vendorclassid) > 255) { + BLog(BLOG_ERROR, "vendorclassid too long"); + goto fail0; + } + if (o->clientid && o->clientid_len > 255) { + BLog(BLOG_ERROR, "clientid too long"); + goto fail0; + } + + // allocate buffers + if (!(o->send_buf = BAlloc(PacketPassInterface_GetMTU(send_if)))) { + BLog(BLOG_ERROR, "BAlloc send buf failed"); + goto fail0; + } + if (!(o->recv_buf = BAlloc(PacketRecvInterface_GetMTU(recv_if)))) { + BLog(BLOG_ERROR, "BAlloc recv buf failed"); + goto fail1; + } + + // init send interface + PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_handler_done, o); + + // init receive interface + PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_handler_done, o); + + // set not sending + o->sending = 0; + + // init timers + BTimer_Init(&o->reset_timer, RESET_TIMEOUT, (BTimer_handler)reset_timer_handler, o); + BTimer_Init(&o->request_timer, REQUEST_TIMEOUT, (BTimer_handler)request_timer_handler, o); + BTimer_Init(&o->renew_timer, 0, (BTimer_handler)renew_timer_handler, o); + BTimer_Init(&o->renew_request_timer, RENEW_REQUEST_TIMEOUT, (BTimer_handler)renew_request_timer_handler, o); + BTimer_Init(&o->lease_timer, 0, (BTimer_handler)lease_timer_handler, o); + + // start receving + PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)o->recv_buf); + + // start + start_process(o, 1); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + BFree(o->send_buf); +fail0: + BFree(o->clientid); + free(o->vendorclassid); + free(o->hostname); + return 0; +} + +void BDHCPClientCore_Free (BDHCPClientCore *o) +{ + DebugObject_Free(&o->d_obj); + + // free timers + BReactor_RemoveTimer(o->reactor, &o->lease_timer); + BReactor_RemoveTimer(o->reactor, &o->renew_request_timer); + BReactor_RemoveTimer(o->reactor, &o->renew_timer); + BReactor_RemoveTimer(o->reactor, &o->request_timer); + BReactor_RemoveTimer(o->reactor, &o->reset_timer); + + // free buffers + BFree(o->recv_buf); + BFree(o->send_buf); + + // free options + BFree(o->clientid); + free(o->vendorclassid); + free(o->hostname); +} + +void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + *out_ip = o->offered.yiaddr; +} + +void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + *out_mask = o->acked.subnet_mask; +} + +int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + if (!o->acked.have_router) { + return 0; + } + + *out_router = o->acked.router; + return 1; +} + +int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers) +{ + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + DebugObject_Access(&o->d_obj); + + int num_return = bmin_int(o->acked.domain_name_servers_count, max_dns_servers); + + memcpy(out_dns_servers, o->acked.domain_name_servers, num_return * sizeof(uint32_t)); + return num_return; +} + +void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_FINISHED || o->state == STATE_RENEWING) + + memcpy(out_mac, o->acked.server_mac, 6); +} diff --git a/external/badvpn_dns/dhcpclient/BDHCPClientCore.h b/external/badvpn_dns/dhcpclient/BDHCPClientCore.h new file mode 100644 index 00000000..98cda365 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/BDHCPClientCore.h @@ -0,0 +1,114 @@ +/** + * @file BDHCPClientCore.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client, excluding system-dependent details. + */ + +#ifndef BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H +#define BADVPN_DHCPCLIENT_BDHCPCLIENTCORE_H + +#include +#include + +#include +#include +#include +#include +#include + +#define BDHCPCLIENTCORE_EVENT_UP 1 +#define BDHCPCLIENTCORE_EVENT_DOWN 2 + +#define BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS 16 + +typedef void (*BDHCPClientCore_func_getsendermac) (void *user, uint8_t *out_mac); +typedef void (*BDHCPClientCore_handler) (void *user, int event); + +struct BDHCPClientCore_opts { + const char *hostname; + const char *vendorclassid; + const uint8_t *clientid; + size_t clientid_len; +}; + +typedef struct { + PacketPassInterface *send_if; + PacketRecvInterface *recv_if; + uint8_t client_mac_addr[6]; + BReactor *reactor; + BRandom2 *random2; + void *user; + BDHCPClientCore_func_getsendermac func_getsendermac; + BDHCPClientCore_handler handler; + char *hostname; + char *vendorclassid; + uint8_t *clientid; + size_t clientid_len; + char *send_buf; + char *recv_buf; + int sending; + BTimer reset_timer; + BTimer request_timer; + BTimer renew_timer; + BTimer renew_request_timer; + BTimer lease_timer; + int state; + int request_count; + uint32_t xid; + int xid_reuse_counter; + struct { + uint32_t yiaddr; + uint32_t dhcp_server_identifier; + } offered; + struct { + uint32_t ip_address_lease_time; + uint32_t subnet_mask; + int have_router; + uint32_t router; + int domain_name_servers_count; + uint32_t domain_name_servers[BDHCPCLIENTCORE_MAX_DOMAIN_NAME_SERVERS]; + uint8_t server_mac[6]; + } acked; + DebugObject d_obj; +} BDHCPClientCore; + +int BDHCPClientCore_Init (BDHCPClientCore *o, PacketPassInterface *send_if, PacketRecvInterface *recv_if, + uint8_t *client_mac_addr, struct BDHCPClientCore_opts opts, BReactor *reactor, + BRandom2 *random2, void *user, + BDHCPClientCore_func_getsendermac func_getsendermac, + BDHCPClientCore_handler handler); +void BDHCPClientCore_Free (BDHCPClientCore *o); +void BDHCPClientCore_GetClientIP (BDHCPClientCore *o, uint32_t *out_ip); +void BDHCPClientCore_GetClientMask (BDHCPClientCore *o, uint32_t *out_mask); +int BDHCPClientCore_GetRouter (BDHCPClientCore *o, uint32_t *out_router); +int BDHCPClientCore_GetDNS (BDHCPClientCore *o, uint32_t *out_dns_servers, size_t max_dns_servers); +void BDHCPClientCore_GetServerMAC (BDHCPClientCore *o, uint8_t *out_mac); + +#endif diff --git a/external/badvpn_dns/dhcpclient/CMakeLists.txt b/external/badvpn_dns/dhcpclient/CMakeLists.txt new file mode 100644 index 00000000..5fd13005 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/CMakeLists.txt @@ -0,0 +1,10 @@ +badvpn_add_library(dhcpclientcore "system;flow;flowextra;badvpn_random" "" BDHCPClientCore.c) + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(DHCPCLIENT_SOURCES + BDHCPClient.c + DHCPIpUdpEncoder.c + DHCPIpUdpDecoder.c + ) + badvpn_add_library(dhcpclient "system;flow;dhcpclientcore" "" "${DHCPCLIENT_SOURCES}") +endif () diff --git a/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.c b/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.c new file mode 100644 index 00000000..1d1c7a75 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.c @@ -0,0 +1,137 @@ +/** + * @file DHCPIpUdpDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static void input_handler_send (DHCPIpUdpDecoder *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + struct ipv4_header iph; + uint8_t *pl; + int pl_len; + + if (!ipv4_check(data, data_len, &iph, &pl, &pl_len)) { + goto fail; + } + + if (ntoh8(iph.protocol) != IPV4_PROTOCOL_UDP) { + goto fail; + } + + if (pl_len < sizeof(struct udp_header)) { + goto fail; + } + struct udp_header udph; + memcpy(&udph, pl, sizeof(udph)); + + if (ntoh16(udph.source_port) != DHCP_SERVER_PORT) { + goto fail; + } + + if (ntoh16(udph.dest_port) != DHCP_CLIENT_PORT) { + goto fail; + } + + int udph_length = ntoh16(udph.length); + if (udph_length < sizeof(udph)) { + goto fail; + } + if (udph_length > data_len - (pl - data)) { + goto fail; + } + + if (ntoh16(udph.checksum) != 0) { + uint16_t checksum_in_packet = udph.checksum; + udph.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udph, pl + sizeof(udph), udph_length - sizeof(udph), iph.source_address, iph.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + } + + // pass payload to output + PacketPassInterface_Sender_Send(o->output, pl + sizeof(udph), udph_length - sizeof(udph)); + + return; + +fail: + PacketPassInterface_Done(&o->input); +} + +static void output_handler_done (DHCPIpUdpDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Done(&o->input); +} + +void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) <= INT_MAX - IPUDP_HEADER_SIZE) + + // init arguments + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init input + PacketPassInterface_Init(&o->input, IPUDP_HEADER_SIZE + PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.h b/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.h new file mode 100644 index 00000000..ce921386 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/DHCPIpUdpDecoder.h @@ -0,0 +1,49 @@ +/** + * @file DHCPIpUdpDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H +#define BADVPN_DHCPCLIENT_DHCPIPUDPDECODER_H + +#include + +#include +#include + +typedef struct { + PacketPassInterface *output; + PacketPassInterface input; + uint8_t *data; + DebugObject d_obj; +} DHCPIpUdpDecoder; + +void DHCPIpUdpDecoder_Init (DHCPIpUdpDecoder *o, PacketPassInterface *output, BPendingGroup *pg); +void DHCPIpUdpDecoder_Free (DHCPIpUdpDecoder *o); +PacketPassInterface * DHCPIpUdpDecoder_GetInput (DHCPIpUdpDecoder *o); + +#endif diff --git a/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.c b/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.c new file mode 100644 index 00000000..da6645c1 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.c @@ -0,0 +1,119 @@ +/** + * @file DHCPIpUdpEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define IPUDP_HEADER_SIZE (sizeof(struct ipv4_header) + sizeof(struct udp_header)) + +static void output_handler_recv (DHCPIpUdpEncoder *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + // remember output packet + o->data = data; + + // receive payload + PacketRecvInterface_Receiver_Recv(o->input, o->data + IPUDP_HEADER_SIZE); +} + +static void input_handler_done (DHCPIpUdpEncoder *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // build IP header + struct ipv4_header iph; + iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph)); + iph.ds = hton8(0); + iph.total_length = hton16(IPUDP_HEADER_SIZE + data_len); + iph.identification = hton16(0); + iph.flags3_fragmentoffset13 = hton16(0); + iph.ttl = hton8(64); + iph.protocol = hton8(IPV4_PROTOCOL_UDP); + iph.checksum = hton16(0); + iph.source_address = hton32(0x00000000); + iph.destination_address = hton32(0xFFFFFFFF); + iph.checksum = ipv4_checksum(&iph, NULL, 0); + + // write UDP header + struct udp_header udph; + udph.source_port = hton16(DHCP_CLIENT_PORT); + udph.dest_port = hton16(DHCP_SERVER_PORT); + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_checksum(&udph, o->data + IPUDP_HEADER_SIZE, data_len, iph.source_address, iph.destination_address); + + // write header + memcpy(o->data, &iph, sizeof(iph)); + memcpy(o->data + sizeof(iph), &udph, sizeof(udph)); + + // finish packet + PacketRecvInterface_Done(&o->output, IPUDP_HEADER_SIZE + data_len); +} + +void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= INT_MAX - IPUDP_HEADER_SIZE) + + // init arguments + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketRecvInterface_Init(&o->output, IPUDP_HEADER_SIZE + PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.h b/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.h new file mode 100644 index 00000000..d3e0ac06 --- /dev/null +++ b/external/badvpn_dns/dhcpclient/DHCPIpUdpEncoder.h @@ -0,0 +1,49 @@ +/** + * @file DHCPIpUdpEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H +#define BADVPN_DHCPCLIENT_DHCPIPUDPENCODER_H + +#include + +#include +#include + +typedef struct { + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *data; + DebugObject d_obj; +} DHCPIpUdpEncoder; + +void DHCPIpUdpEncoder_Init (DHCPIpUdpEncoder *o, PacketRecvInterface *input, BPendingGroup *pg); +void DHCPIpUdpEncoder_Free (DHCPIpUdpEncoder *o); +PacketRecvInterface * DHCPIpUdpEncoder_GetOutput (DHCPIpUdpEncoder *o); + +#endif diff --git a/external/badvpn_dns/dostest/CMakeLists.txt b/external/badvpn_dns/dostest/CMakeLists.txt new file mode 100644 index 00000000..8d3b7422 --- /dev/null +++ b/external/badvpn_dns/dostest/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(dostest-server + dostest-server.c + StreamBuffer.c +) +target_link_libraries(dostest-server base system) + +add_executable(dostest-attacker + dostest-attacker.c +) +target_link_libraries(dostest-attacker base system) diff --git a/external/badvpn_dns/dostest/StreamBuffer.c b/external/badvpn_dns/dostest/StreamBuffer.c new file mode 100644 index 00000000..d4391276 --- /dev/null +++ b/external/badvpn_dns/dostest/StreamBuffer.c @@ -0,0 +1,147 @@ +/** + * @file StreamBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "StreamBuffer.h" + +// called when receive operation is complete +static void input_handler_done (void *vo, int data_len) +{ + StreamBuffer *o = (StreamBuffer *)vo; + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - (o->buf_start + o->buf_used)) + + // remember if buffer was empty + int was_empty = (o->buf_used == 0); + + // increment buf_used by the amount that was received + o->buf_used += data_len; + + // start another receive operation unless buffer is full + if (o->buf_used < o->buf_size - o->buf_start) { + int end = o->buf_start + o->buf_used; + StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, o->buf_size - end); + } + else if (o->buf_used < o->buf_size) { + // wrap around + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_start); + } + + // if buffer was empty before, start send operation + if (was_empty) { + StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, o->buf_used); + } +} + +// called when send operation is complete +static void output_handler_done (void *vo, int data_len) +{ + StreamBuffer *o = (StreamBuffer *)vo; + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_used) + ASSERT(data_len <= o->buf_size - o->buf_start) + + // remember if buffer was full + int was_full = (o->buf_used == o->buf_size); + + // increment buf_start and decrement buf_used by the + // amount that was sent + o->buf_start += data_len; + o->buf_used -= data_len; + + // wrap around buf_start + if (o->buf_start == o->buf_size) { + o->buf_start = 0; + } + + // start receive operation if buffer was full + if (was_full) { + int end; + int avail; + if (o->buf_used >= o->buf_size - o->buf_start) { + end = o->buf_used - (o->buf_size - o->buf_start); + avail = o->buf_start - end; + } else { + end = o->buf_start + o->buf_used; + avail = o->buf_size - end; + } + StreamRecvInterface_Receiver_Recv(o->input, o->buf + end, avail); + } + + // start another receive send unless buffer is empty + if (o->buf_used > 0) { + int to_send = bmin_int(o->buf_used, o->buf_size - o->buf_start); + StreamPassInterface_Sender_Send(o->output, o->buf + o->buf_start, to_send); + } +} + +int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output) +{ + ASSERT(buf_size > 0) + ASSERT(input) + ASSERT(output) + + // remember arguments + o->buf_size = buf_size; + o->input = input; + o->output = output; + + // allocate buffer memory + o->buf = (uint8_t *)BAllocSize(bsize_fromint(o->buf_size)); + if (!o->buf) { + goto fail0; + } + + // set initial buffer state + o->buf_start = 0; + o->buf_used = 0; + + // set receive and send done callbacks + StreamRecvInterface_Receiver_Init(o->input, input_handler_done, o); + StreamPassInterface_Sender_Init(o->output, output_handler_done, o); + + // start receive operation + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void StreamBuffer_Free (StreamBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer memory + BFree(o->buf); +} diff --git a/external/badvpn_dns/dostest/StreamBuffer.h b/external/badvpn_dns/dostest/StreamBuffer.h new file mode 100644 index 00000000..dd441f5b --- /dev/null +++ b/external/badvpn_dns/dostest/StreamBuffer.h @@ -0,0 +1,70 @@ +/** + * @file StreamBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STREAMBUFFER_H +#define BADVPN_STREAMBUFFER_H + +#include +#include +#include +#include + +/** + * Buffer object which reads data from a \link StreamRecvInterface and writes + * it to a \link StreamPassInterface. + */ +typedef struct { + int buf_size; + StreamRecvInterface *input; + StreamPassInterface *output; + uint8_t *buf; + int buf_start; + int buf_used; + DebugObject d_obj; +} StreamBuffer; + +/** + * Initializes the buffer object. + * + * @param o object to initialize + * @param buf_size size of the buffer. Must be >0. + * @param input input interface + * @param outout output interface + * @return 1 on success, 0 on failure + */ +int StreamBuffer_Init (StreamBuffer *o, int buf_size, StreamRecvInterface *input, StreamPassInterface *output) WARN_UNUSED; + +/** + * Frees the buffer object. + * + * @param o object to free + */ +void StreamBuffer_Free (StreamBuffer *o); + +#endif diff --git a/external/badvpn_dns/dostest/dostest-attacker.c b/external/badvpn_dns/dostest/dostest-attacker.c new file mode 100644 index 00000000..723dadd8 --- /dev/null +++ b/external/badvpn_dns/dostest/dostest-attacker.c @@ -0,0 +1,512 @@ +/** + * @file dostest-attacker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PROGRAM_NAME "dostest-attacker" + +// connection structure +struct connection { + int connected; + BConnector connector; + BConnection con; + StreamRecvInterface *recv_if; + uint8_t buf[512]; + LinkedList1Node connections_list_node; +}; + +// command-line options +static struct { + int help; + int version; + char *connect_addr; + int max_connections; + int max_connecting; + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; +} options; + +// connect address +static BAddr connect_addr; + +// reactor +static BReactor reactor; + +// connections +static LinkedList1 connections_list; +static int num_connections; +static int num_connecting; + +// timer for scheduling creation of more connections +static BTimer make_connections_timer; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static int connection_new (void); +static void connection_free (struct connection *conn); +static void connection_logfunc (struct connection *conn); +static void connection_log (struct connection *conn, int level, const char *fmt, ...); +static void connection_connector_handler (struct connection *conn, int is_error); +static void connection_connection_handler (struct connection *conn, int event); +static void connection_recv_handler_done (struct connection *conn, int data_len); +static void make_connections_timer_handler (void *unused); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // init loger + BLog_InitStderr(); + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // init connections list + LinkedList1_Init(&connections_list); + num_connections = 0; + num_connecting = 0; + + // init make connections timer + BTimer_Init(&make_connections_timer, 0, make_connections_timer_handler, NULL); + BReactor_SetTimer(&reactor, &make_connections_timer); + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&reactor); + + // free connections + while (!LinkedList1_IsEmpty(&connections_list)) { + struct connection *conn = UPPER_OBJECT(LinkedList1_GetFirst(&connections_list), struct connection, connections_list_node); + connection_free(conn); + } + // free make connections timer + BReactor_RemoveTimer(&reactor, &make_connections_timer); + // free signal + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&reactor); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " --connect-addr \n" + " --max-connections \n" + " --max-connecting \n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.connect_addr = NULL; + options.max_connections = -1; + options.max_connecting = -1; + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--connect-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.connect_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--max-connections")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connections = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-connecting")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connecting = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.connect_addr) { + fprintf(stderr, "--connect-addr missing\n"); + return 0; + } + + if (options.max_connections == -1) { + fprintf(stderr, "--max-connections missing\n"); + return 0; + } + + if (options.max_connecting == -1) { + fprintf(stderr, "--max-connecting missing\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen address + if (!BAddr_Parse(&connect_addr, options.connect_addr, NULL, 0)) { + BLog(BLOG_ERROR, "connect addr: BAddr_Parse failed"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&reactor, 1); +} + +int connection_new (void) +{ + // allocate structure + struct connection *conn = (struct connection *)malloc(sizeof(*conn)); + if (!conn) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set not connected + conn->connected = 0; + + // init connector + if (!BConnector_Init(&conn->connector, connect_addr, &reactor, conn, (BConnector_handler)connection_connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail1; + } + + // add to connections list + LinkedList1_Append(&connections_list, &conn->connections_list_node); + num_connections++; + num_connecting++; + + return 1; + +fail1: + free(conn); +fail0: + return 0; +} + +void connection_free (struct connection *conn) +{ + // remove from connections list + LinkedList1_Remove(&connections_list, &conn->connections_list_node); + num_connections--; + if (!conn->connected) { + num_connecting--; + } + + if (conn->connected) { + // free receive interface + BConnection_RecvAsync_Free(&conn->con); + + // free connection + BConnection_Free(&conn->con); + } + + // free connector + BConnector_Free(&conn->connector); + + // free structure + free(conn); +} + +void connection_logfunc (struct connection *conn) +{ + BLog_Append("%d connection (%p): ", num_connecting, (void *)conn); +} + +void connection_log (struct connection *conn, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)connection_logfunc, conn, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void connection_connector_handler (struct connection *conn, int is_error) +{ + ASSERT(!conn->connected) + + // check for connection error + if (is_error) { + connection_log(conn, BLOG_INFO, "failed to connect"); + goto fail0; + } + + // init connection from connector + if (!BConnection_Init(&conn->con, BConnection_source_connector(&conn->connector), &reactor, conn, (BConnection_handler)connection_connection_handler)) { + connection_log(conn, BLOG_INFO, "BConnection_Init failed"); + goto fail0; + } + + // init receive interface + BConnection_RecvAsync_Init(&conn->con); + conn->recv_if = BConnection_RecvAsync_GetIf(&conn->con); + StreamRecvInterface_Receiver_Init(conn->recv_if, (StreamRecvInterface_handler_done)connection_recv_handler_done, conn); + + // start receiving + StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf)); + + // no longer connecting + conn->connected = 1; + num_connecting--; + + connection_log(conn, BLOG_INFO, "connected"); + + // schedule making connections (because of connecting limit) + BReactor_SetTimer(&reactor, &make_connections_timer); + return; + +fail0: + // free connection + connection_free(conn); + + // schedule making connections + BReactor_SetTimer(&reactor, &make_connections_timer); +} + +void connection_connection_handler (struct connection *conn, int event) +{ + ASSERT(conn->connected) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + connection_log(conn, BLOG_INFO, "connection closed"); + } else { + connection_log(conn, BLOG_INFO, "connection error"); + } + + // free connection + connection_free(conn); + + // schedule making connections + BReactor_SetTimer(&reactor, &make_connections_timer); +} + +void connection_recv_handler_done (struct connection *conn, int data_len) +{ + ASSERT(conn->connected) + + // receive more + StreamRecvInterface_Receiver_Recv(conn->recv_if, conn->buf, sizeof(conn->buf)); + + connection_log(conn, BLOG_INFO, "received %d bytes", data_len); +} + +void make_connections_timer_handler (void *unused) +{ + int make_num = bmin_int(options.max_connections - num_connections, options.max_connecting - num_connecting); + + if (make_num <= 0) { + return; + } + + BLog(BLOG_INFO, "making %d connections", make_num); + + for (int i = 0; i < make_num; i++) { + if (!connection_new()) { + // can happen if fd limit is reached + BLog(BLOG_ERROR, "failed to make connection, waiting"); + BReactor_SetTimerAfter(&reactor, &make_connections_timer, 10); + return; + } + } +} diff --git a/external/badvpn_dns/dostest/dostest-server.c b/external/badvpn_dns/dostest/dostest-server.c new file mode 100644 index 00000000..74475914 --- /dev/null +++ b/external/badvpn_dns/dostest/dostest-server.c @@ -0,0 +1,567 @@ +/** + * @file dostest-server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#ifdef BADVPN_LINUX +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "StreamBuffer.h" + +#include + +#define PROGRAM_NAME "dostest-server" + +#ifdef BADVPN_LINUX +#ifndef SO_DOSDEF_PREPARE +#define SO_DOSDEF_PREPARE 48 +#endif +#ifndef SO_DOSDEF_ACTIVATE +#define SO_DOSDEF_ACTIVATE 49 +#endif +#endif + +#define BUF_SIZE 1024 + +// client structure +struct client { + BConnection con; + BAddr addr; + StreamBuffer buf; + BTimer disconnect_timer; + LinkedList1Node clients_list_node; +}; + +// command-line options +static struct { + int help; + int version; + char *listen_addr; + int max_clients; + int disconnect_time; + int defense_prepare_clients; + int defense_activate_clients; + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; +} options; + +// listen address +static BAddr listen_addr; + +// reactor +static BReactor ss; + +// listener +static BListener listener; + +// clients +static LinkedList1 clients_list; +static int num_clients; + +// defense status +static int defense_prepare; +static int defense_activate; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static void listener_handler (void *unused); +static void client_free (struct client *client); +static void client_logfunc (struct client *client); +static void client_log (struct client *client, int level, const char *fmt, ...); +static void client_disconnect_timer_handler (struct client *client); +static void client_connection_handler (struct client *client, int event); +static void update_defense (void); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // init loger + BLog_InitStderr(); + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // initialize listener + if (!BListener_Init(&listener, listen_addr, &ss, NULL, listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail3; + } + + // init clients list + LinkedList1_Init(&clients_list); + num_clients = 0; + + // clear defense state + defense_prepare = 0; + defense_activate = 0; + + // update defense + update_defense(); + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + while (!LinkedList1_IsEmpty(&clients_list)) { + struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node); + client_free(client); + } + // free listener + BListener_Free(&listener); +fail3: + // free signal + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&ss); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " --listen-addr \n" + " --max-clients \n" + " --disconnect-time \n" + " [--defense-prepare-clients ]\n" + " [--defense-activate-clients ]\n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.listen_addr = NULL; + options.max_clients = -1; + options.disconnect_time = -1; + options.defense_prepare_clients = -1; + options.defense_activate_clients = -1; + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.listen_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--disconnect-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.disconnect_time = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--defense-prepare-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.defense_prepare_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--defense-activate-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.defense_activate_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.listen_addr) { + fprintf(stderr, "--listen-addr missing\n"); + return 0; + } + + if (options.max_clients == -1) { + fprintf(stderr, "--max-clients missing\n"); + return 0; + } + + if (options.disconnect_time == -1) { + fprintf(stderr, "--disconnect-time missing\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen address + if (!BAddr_Parse(&listen_addr, options.listen_addr, NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void listener_handler (void *unused) +{ + if (num_clients == options.max_clients) { + BLog(BLOG_ERROR, "maximum number of clients reached"); + goto fail0; + } + + // allocate structure + struct client *client = (struct client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // accept client + if (!BConnection_Init(&client->con, BConnection_source_listener(&listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init connection interfaces + BConnection_RecvAsync_Init(&client->con); + BConnection_SendAsync_Init(&client->con); + StreamRecvInterface *recv_if = BConnection_RecvAsync_GetIf(&client->con); + StreamPassInterface *send_if = BConnection_SendAsync_GetIf(&client->con); + + // init stream buffer (to loop received data back to the client) + if (!StreamBuffer_Init(&client->buf, BUF_SIZE, recv_if, send_if)) { + BLog(BLOG_ERROR, "StreamBuffer_Init failed"); + goto fail2; + } + + // init disconnect timer + BTimer_Init(&client->disconnect_timer, options.disconnect_time, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // insert to clients list + LinkedList1_Append(&clients_list, &client->clients_list_node); + num_clients++; + + client_log(client, BLOG_INFO, "connected"); + BLog(BLOG_NOTICE, "%d clients", num_clients); + + // update defense + update_defense(); + return; + +fail2: + BConnection_SendAsync_Free(&client->con); + BConnection_RecvAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_free (struct client *client) +{ + // remove from clients list + LinkedList1_Remove(&clients_list, &client->clients_list_node); + num_clients--; + + // free disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free stream buffer + StreamBuffer_Free(&client->buf); + + // free connection interfaces + BConnection_SendAsync_Free(&client->con); + BConnection_RecvAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free structure + free(client); + + BLog(BLOG_NOTICE, "%d clients", num_clients); + + // update defense + update_defense(); +} + +void client_logfunc (struct client *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client (%s): ", addr); +} + +void client_log (struct client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client *client) +{ + client_log(client, BLOG_INFO, "timed out, disconnecting"); + + // free client + client_free(client); +} + +void client_connection_handler (struct client *client, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "client closed"); + } else { + client_log(client, BLOG_INFO, "client error"); + } + + // free client + client_free(client); +} + +void update_defense (void) +{ +#ifdef BADVPN_LINUX + if (options.defense_prepare_clients != -1) { + int val = num_clients >= options.defense_prepare_clients; + int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_PREPARE, &val, sizeof(val)); + if (res < 0) { + BLog(BLOG_ERROR, "failed to %s defense preparation", (val ? "enable" : "disable")); + } else { + if (!defense_prepare && val) { + BLog(BLOG_NOTICE, "defense preparation enabled"); + } + else if (defense_prepare && !val) { + BLog(BLOG_NOTICE, "defense preparation disabled"); + } + } + defense_prepare = val; + } + + if (options.defense_activate_clients != -1) { + int val = num_clients >= options.defense_activate_clients; + int res = setsockopt(listener.fd, SOL_SOCKET, SO_DOSDEF_ACTIVATE, &val, sizeof(val)); + if (res < 0) { + BLog(BLOG_ERROR, "failed to %s defense activation", (val ? "enable" : "disable")); + } else { + if (!defense_activate && val) { + BLog(BLOG_NOTICE, "defense activation enabled"); + } + else if (defense_activate && !val) { + BLog(BLOG_NOTICE, "defense activation disabled"); + } + } + defense_activate = val; + } +#endif +} diff --git a/external/badvpn_dns/examples/CMakeLists.txt b/external/badvpn_dns/examples/CMakeLists.txt new file mode 100644 index 00000000..27dbeaa9 --- /dev/null +++ b/external/badvpn_dns/examples/CMakeLists.txt @@ -0,0 +1,97 @@ +if (NOT EMSCRIPTEN) + add_executable(btimer_example btimer_example.c) + target_link_libraries(btimer_example system) +endif () + +if (BUILDING_PREDICATE) + add_executable(predicate_test predicate_test.c) + target_link_libraries(predicate_test predicate) +endif () + +if (NOT EMSCRIPTEN) + add_executable(fairqueue_test fairqueue_test.c) + target_link_libraries(fairqueue_test system flow) +endif () + +add_executable(indexedlist_test indexedlist_test.c) + +if (BUILDING_SECURITY) + add_executable(fairqueue_test2 fairqueue_test2.c) + target_link_libraries(fairqueue_test2 system flow security) + + add_executable(bavl_test bavl_test.c) + target_link_libraries(bavl_test security) + + add_executable(savl_test savl_test.c) + target_link_libraries(savl_test security) + + add_executable(bencryption_bench bencryption_bench.c) + target_link_libraries(bencryption_bench system security) +endif () + +if (BUILD_NCD) + add_executable(ncd_tokenizer_test ncd_tokenizer_test.c) + target_link_libraries(ncd_tokenizer_test ncdtokenizer) + + add_executable(ncd_parser_test ncd_parser_test.c) + target_link_libraries(ncd_parser_test ncdconfigparser ncdvalgenerator ncdsugar) + + add_executable(ncd_value_parser_test ncd_value_parser_test.c) + target_link_libraries(ncd_value_parser_test ncdvalparser ncdvalgenerator) + + if (NOT EMSCRIPTEN) + add_executable(ncdinterfacemonitor_test ncdinterfacemonitor_test.c) + target_link_libraries(ncdinterfacemonitor_test ncdinterfacemonitor) + endif () + + add_executable(ncdval_test ncdval_test.c) + target_link_libraries(ncdval_test ncdval) + + add_executable(ncdvalcons_test ncdvalcons_test.c) + target_link_libraries(ncdvalcons_test ncdvalcons ncdvalgenerator) +endif () + +if (BUILDING_UDEVMONITOR) + add_executable(ncdudevmonitor_test ncdudevmonitor_test.c) + target_link_libraries(ncdudevmonitor_test udevmonitor) + + add_executable(ncdudevmanager_test ncdudevmanager_test.c) + target_link_libraries(ncdudevmanager_test udevmonitor) +endif () + +if (NOT WIN32 AND NOT EMSCRIPTEN) + add_executable(bprocess_example bprocess_example.c) + target_link_libraries(bprocess_example system) + + add_executable(stdin_input stdin_input.c) + target_link_libraries(stdin_input system flow flowextra) +endif () + +if (BUILDING_DHCPCLIENT) + add_executable(dhcpclient_test dhcpclient_test.c) + target_link_libraries(dhcpclient_test dhcpclient) +endif () + +if (BUILDING_ARPPROBE) + add_executable(arpprobe_test arpprobe_test.c) + target_link_libraries(arpprobe_test arpprobe) +endif () + +add_executable(substring_test substring_test.c) + +if (NOT WIN32) + add_executable(ipaddr6_test ipaddr6_test.c) + add_executable(parse_number_test parse_number_test.c) +endif () + +if (BUILDING_RANDOM) + add_executable(brandom2_test brandom2_test.c) + target_link_libraries(brandom2_test badvpn_random) +endif () + +add_executable(cavl_test cavl_test.c) + +if (EMSCRIPTEN) + add_executable(emscripten_test emscripten_test.c) + target_link_libraries(emscripten_test system) +endif () diff --git a/external/badvpn_dns/examples/FastPacketSource.h b/external/badvpn_dns/examples/FastPacketSource.h new file mode 100644 index 00000000..e13e2f2c --- /dev/null +++ b/external/badvpn_dns/examples/FastPacketSource.h @@ -0,0 +1,79 @@ +/** + * @file FastPacketSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FASTPACKETSOURCE_H +#define _FASTPACKETSOURCE_H + +#include +#include + +#include +#include +#include + +typedef struct { + PacketPassInterface *output; + int psize; + uint8_t *data; + int data_len; + DebugObject d_obj; +} FastPacketSource; + +static void _FastPacketSource_output_handler_done (FastPacketSource *s) +{ + DebugObject_Access(&s->d_obj); + + PacketPassInterface_Sender_Send(s->output, s->data, s->data_len); +} + +static void FastPacketSource_Init (FastPacketSource *s, PacketPassInterface *output, uint8_t *data, int data_len, BPendingGroup *pg) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= PacketPassInterface_GetMTU(output)); + + // init arguments + s->output = output; + s->data = data; + s->data_len = data_len; + + // init output + PacketPassInterface_Sender_Init(s->output, (PacketPassInterface_handler_done)_FastPacketSource_output_handler_done, s); + + // schedule send + PacketPassInterface_Sender_Send(s->output, s->data, s->data_len); + + DebugObject_Init(&s->d_obj); +} + +static void FastPacketSource_Free (FastPacketSource *s) +{ + DebugObject_Free(&s->d_obj); +} + +#endif diff --git a/external/badvpn_dns/examples/RandomPacketSink.h b/external/badvpn_dns/examples/RandomPacketSink.h new file mode 100644 index 00000000..cbadf787 --- /dev/null +++ b/external/badvpn_dns/examples/RandomPacketSink.h @@ -0,0 +1,116 @@ +/** + * @file RandomPacketSink.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _RANDOMPACKETSINK_H +#define _RANDOMPACKETSINK_H + +#include + +#include +#include +#include +#include +#include + +typedef struct { + BReactor *reactor; + PacketPassInterface input; + BTimer timer; + DebugObject d_obj; +} RandomPacketSink; + +static void _RandomPacketSink_input_handler_send (RandomPacketSink *s, uint8_t *data, int data_len) +{ + DebugObject_Access(&s->d_obj); + + printf("sink: send '"); + size_t res = fwrite(data, data_len, 1, stdout); + B_USE(res) + + uint8_t r; + BRandom_randomize(&r, sizeof(r)); + if (r&(uint8_t)1) { + printf("' accepting\n"); + PacketPassInterface_Done(&s->input); + } else { + printf("' delaying\n"); + BReactor_SetTimer(s->reactor, &s->timer); + } +} + +static void _RandomPacketSink_input_handler_requestcancel (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + printf("sink: cancelled\n"); + BReactor_RemoveTimer(s->reactor, &s->timer); + PacketPassInterface_Done(&s->input); +} + +static void _RandomPacketSink_timer_handler (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + PacketPassInterface_Done(&s->input); +} + +static void RandomPacketSink_Init (RandomPacketSink *s, BReactor *reactor, int mtu, int ms) +{ + // init arguments + s->reactor = reactor; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_RandomPacketSink_input_handler_send, s, BReactor_PendingGroup(reactor)); + PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_RandomPacketSink_input_handler_requestcancel); + + // init timer + BTimer_Init(&s->timer, ms, (BTimer_handler)_RandomPacketSink_timer_handler, s); + + DebugObject_Init(&s->d_obj); +} + +static void RandomPacketSink_Free (RandomPacketSink *s) +{ + DebugObject_Free(&s->d_obj); + + // free timer + BReactor_RemoveTimer(s->reactor, &s->timer); + + // free input + PacketPassInterface_Free(&s->input); +} + +static PacketPassInterface * RandomPacketSink_GetInput (RandomPacketSink *s) +{ + DebugObject_Access(&s->d_obj); + + return &s->input; +} + +#endif diff --git a/external/badvpn_dns/examples/TimerPacketSink.h b/external/badvpn_dns/examples/TimerPacketSink.h new file mode 100644 index 00000000..e1e82172 --- /dev/null +++ b/external/badvpn_dns/examples/TimerPacketSink.h @@ -0,0 +1,97 @@ +/** + * @file TimerPacketSink.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TIMERPACKETSINK_H +#define _TIMERPACKETSINK_H + +#include + +#include +#include +#include + +typedef struct { + BReactor *reactor; + PacketPassInterface input; + BTimer timer; +} TimerPacketSink; + +static void _TimerPacketSink_input_handler_send (TimerPacketSink *s, uint8_t *data, int data_len) +{ + printf("sink: send '"); + size_t res = fwrite(data, data_len, 1, stdout); + B_USE(res) + printf("'\n"); + + BReactor_SetTimer(s->reactor, &s->timer); +} + +static void _TimerPacketSink_input_handler_requestcancel (TimerPacketSink *s) +{ + printf("sink: cancelled\n"); + + BReactor_RemoveTimer(s->reactor, &s->timer); + PacketPassInterface_Done(&s->input); +} + +static void _TimerPacketSink_timer_handler (TimerPacketSink *s) +{ + printf("sink: done\n"); + + PacketPassInterface_Done(&s->input); +} + +static void TimerPacketSink_Init (TimerPacketSink *s, BReactor *reactor, int mtu, int ms) +{ + // init arguments + s->reactor = reactor; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)_TimerPacketSink_input_handler_send, s, BReactor_PendingGroup(s->reactor)); + PacketPassInterface_EnableCancel(&s->input, (PacketPassInterface_handler_requestcancel)_TimerPacketSink_input_handler_requestcancel); + + // init timer + BTimer_Init(&s->timer, ms, (BTimer_handler)_TimerPacketSink_timer_handler, s); +} + +static void TimerPacketSink_Free (TimerPacketSink *s) +{ + // free timer + BReactor_RemoveTimer(s->reactor, &s->timer); + + // free input + PacketPassInterface_Free(&s->input); +} + +static PacketPassInterface * TimerPacketSink_GetInput (TimerPacketSink *s) +{ + return &s->input; +} + +#endif diff --git a/external/badvpn_dns/examples/arpprobe_test.c b/external/badvpn_dns/examples/arpprobe_test.c new file mode 100644 index 00000000..d075f523 --- /dev/null +++ b/external/badvpn_dns/examples/arpprobe_test.c @@ -0,0 +1,131 @@ +/** + * @file arpprobe_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BArpProbe arpprobe; + +static void signal_handler (void *user); +static void arpprobe_handler (void *unused, int event); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + goto fail0; + } + + char *ifname = argv[1]; + uint32_t addr = inet_addr(argv[2]); + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + if (!BArpProbe_Init(&arpprobe, ifname, addr, &reactor, NULL, arpprobe_handler)) { + DEBUG("BArpProbe_Init failed"); + goto fail3; + } + + BReactor_Exec(&reactor); + + BArpProbe_Free(&arpprobe); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 0); +} + +void arpprobe_handler (void *unused, int event) +{ + switch (event) { + case BARPPROBE_EVENT_EXIST: { + printf("ARPPROBE: exist\n"); + } break; + + case BARPPROBE_EVENT_NOEXIST: { + printf("ARPPROBE: noexist\n"); + } break; + + case BARPPROBE_EVENT_ERROR: { + printf("ARPPROBE: error\n"); + + // exit reactor + BReactor_Quit(&reactor, 0); + } break; + + default: + ASSERT(0); + } +} diff --git a/external/badvpn_dns/examples/bavl_test.c b/external/badvpn_dns/examples/bavl_test.c new file mode 100644 index 00000000..c30ae6f3 --- /dev/null +++ b/external/badvpn_dns/examples/bavl_test.c @@ -0,0 +1,129 @@ +/** + * @file bavl_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +struct mynode { + int used; + int num; + BAVLNode avl_node; +}; + +static int int_comparator (void *user, int *val1, int *val2) +{ + return B_COMPARE(*val1, *val2); +} + +static void verify (BAVL *tree) +{ + printf("Verifying...\n"); + BAVL_Verify(tree); +} + +int main (int argc, char **argv) +{ + int num_nodes; + int num_random_delete; + + if (argc != 3 || (num_nodes = atoi(argv[1])) <= 0 || (num_random_delete = atoi(argv[2])) < 0) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + return 1; + } + + struct mynode *nodes = (struct mynode *)BAllocArray(num_nodes, sizeof(*nodes)); + ASSERT_FORCE(nodes) + + int *values_ins = (int *)BAllocArray(num_nodes, sizeof(int)); + ASSERT_FORCE(values_ins) + + int *values = (int *)BAllocArray(num_random_delete, sizeof(int)); + ASSERT_FORCE(values) + + BAVL avl; + BAVL_Init(&avl, OFFSET_DIFF(struct mynode, num, avl_node), (BAVL_comparator)int_comparator, NULL); + verify(&avl); + + printf("Inserting random values...\n"); + int inserted = 0; + BRandom_randomize((uint8_t *)values_ins, num_nodes * sizeof(int)); + for (int i = 0; i < num_nodes; i++) { + nodes[i].num = values_ins[i]; + if (BAVL_Insert(&avl, &nodes[i].avl_node, NULL)) { + nodes[i].used = 1; + inserted++; + } else { + nodes[i].used = 0; + printf("Insert collision!\n"); + } + } + printf("Inserted %d entries\n", inserted); + verify(&avl); + + printf("Removing random entries...\n"); + int removed1 = 0; + BRandom_randomize((uint8_t *)values, num_random_delete * sizeof(int)); + for (int i = 0; i < num_random_delete; i++) { + int index = (((unsigned int *)values)[i] % num_nodes); + struct mynode *node = nodes + index; + if (node->used) { + BAVL_Remove(&avl, &node->avl_node); + node->used = 0; + removed1++; + } + } + printf("Removed %d entries\n", removed1); + verify(&avl); + + printf("Removing remaining...\n"); + int removed2 = 0; + while (!BAVL_IsEmpty(&avl)) { + struct mynode *node = UPPER_OBJECT(BAVL_GetFirst(&avl), struct mynode, avl_node); + ASSERT_FORCE(node->used) + BAVL_Remove(&avl, &node->avl_node); + node->used = 0; + removed2++; + } + printf("Removed %d entries\n", removed2); + ASSERT_FORCE(BAVL_IsEmpty(&avl)) + ASSERT_FORCE(removed1 + removed2 == inserted) + verify(&avl); + + BFree(nodes); + BFree(values_ins); + BFree(values); + + return 0; +} diff --git a/external/badvpn_dns/examples/bencryption_bench.c b/external/badvpn_dns/examples/bencryption_bench.c new file mode 100644 index 00000000..c842bf2d --- /dev/null +++ b/external/badvpn_dns/examples/bencryption_bench.c @@ -0,0 +1,146 @@ +/** + * @file bencryption_bench.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void usage (char *name) +{ + printf( + "Usage: %s \n" + " is one of (blowfish, aes).\n", + name + ); + + exit(1); +} + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 5) { + usage(argv[0]); + } + + char *mode_str = argv[1]; + char *cipher_str = argv[2]; + + int mode; + int cipher = 0; // silence warning + int num_blocks = atoi(argv[3]); + int num_ops = atoi(argv[4]); + + if (!strcmp(mode_str, "enc")) { + mode = BENCRYPTION_MODE_ENCRYPT; + } + else if (!strcmp(mode_str, "dec")) { + mode = BENCRYPTION_MODE_DECRYPT; + } + else { + usage(argv[0]); + } + + if (!strcmp(cipher_str, "blowfish")) { + cipher = BENCRYPTION_CIPHER_BLOWFISH; + } + else if (!strcmp(cipher_str, "aes")) { + cipher = BENCRYPTION_CIPHER_AES; + } + else { + usage(argv[0]); + } + + if (num_blocks < 0 || num_ops < 0) { + usage(argv[0]); + } + + int key_size = BEncryption_cipher_key_size(cipher); + int block_size = BEncryption_cipher_block_size(cipher); + + uint8_t key[BENCRYPTION_MAX_KEY_SIZE]; + BRandom_randomize(key, key_size); + + uint8_t iv[BENCRYPTION_MAX_BLOCK_SIZE]; + BRandom_randomize(iv, block_size); + + if (num_blocks > INT_MAX / block_size) { + printf("too much"); + goto fail0; + } + int unit_size = num_blocks * block_size; + + printf("unit size %d\n", unit_size); + + uint8_t *buf1 = (uint8_t *)BAlloc(unit_size); + if (!buf1) { + printf("BAlloc failed"); + goto fail0; + } + + uint8_t *buf2 = (uint8_t *)BAlloc(unit_size); + if (!buf2) { + printf("BAlloc failed"); + goto fail1; + } + + BEncryption enc; + BEncryption_Init(&enc, mode, cipher, key); + + uint8_t *in = buf1; + uint8_t *out = buf2; + BRandom_randomize(in, unit_size); + + for (int i = 0; i < num_ops; i++) { + BEncryption_Encrypt(&enc, in, out, unit_size, iv); + + uint8_t *t = in; + in = out; + out = t; + } + + BEncryption_Free(&enc); + BFree(buf2); +fail1: + BFree(buf1); +fail0: + DebugObjectGlobal_Finish(); + + return 0; +} diff --git a/external/badvpn_dns/examples/bprocess_example.c b/external/badvpn_dns/examples/bprocess_example.c new file mode 100644 index 00000000..0ece996f --- /dev/null +++ b/external/badvpn_dns/examples/bprocess_example.c @@ -0,0 +1,140 @@ +/** + * @file bprocess_example.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BUnixSignal unixsignal; +BProcessManager manager; +BProcess process; + +static void unixsignal_handler (void *user, int signo); +static void process_handler (void *user, int normally, uint8_t normally_exit_status); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + int ret = 1; + + if (argc < 2) { + printf("Usage: %s [argument ...]\n", argv[0]); + goto fail0; + } + + char *program = argv[1]; + + // init time + BTime_Init(); + + // init logger + BLog_InitStdout(); + + // init reactor (event loop) + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + // choose signals to catch + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + + // init BUnixSignal for catching signals + if (!BUnixSignal_Init(&unixsignal, &reactor, set, unixsignal_handler, NULL)) { + DEBUG("BUnixSignal_Init failed"); + goto fail2; + } + + // init process manager + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + char **p_argv = argv + 1; + + // map fds 0, 1, 2 in child to fds 0, 1, 2 in parent + int fds[] = { 0, 1, 2, -1 }; + int fds_map[] = { 0, 1, 2 }; + + // start child process + if (!BProcess_InitWithFds(&process, &manager, process_handler, NULL, program, p_argv, NULL, fds, fds_map)) { + DEBUG("BProcess_Init failed"); + goto fail4; + } + + // enter event loop + ret = BReactor_Exec(&reactor); + + BProcess_Free(&process); +fail4: + BProcessManager_Free(&manager); +fail3: + BUnixSignal_Free(&unixsignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void unixsignal_handler (void *user, int signo) +{ + DEBUG("received %s, terminating child", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // send SIGTERM to child + BProcess_Terminate(&process); +} + +void process_handler (void *user, int normally, uint8_t normally_exit_status) +{ + DEBUG("process terminated"); + + int ret = (normally ? normally_exit_status : 1); + + // return from event loop + BReactor_Quit(&reactor, ret); +} diff --git a/external/badvpn_dns/examples/brandom2_test.c b/external/badvpn_dns/examples/brandom2_test.c new file mode 100644 index 00000000..539735ca --- /dev/null +++ b/external/badvpn_dns/examples/brandom2_test.c @@ -0,0 +1,65 @@ +/** + * @file brandom2_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#define NUM_NUMBERS 10 + +static BRandom2 brandom; + +int main (int argc, char *argv[]) +{ + int ret = 1; + + if (!BRandom2_Init(&brandom, 0)) { + DEBUG("BRandom2_Init failed"); + goto fail0; + } + + uint32_t numbers[NUM_NUMBERS]; + if (!BRandom2_GenBytes(&brandom, numbers, sizeof(numbers))) { + DEBUG("BRandom2_GenBytes failed"); + goto fail1; + } + + for (int i = 0; i < NUM_NUMBERS; i++) { + printf("%"PRIu32"\n", numbers[i]); + } + + ret = 0; + +fail1: + BRandom2_Free(&brandom); +fail0: + return ret; +} diff --git a/external/badvpn_dns/examples/btimer_example.c b/external/badvpn_dns/examples/btimer_example.c new file mode 100644 index 00000000..c4d8d54a --- /dev/null +++ b/external/badvpn_dns/examples/btimer_example.c @@ -0,0 +1,84 @@ +/** + * @file btimer_example.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +// gives average firing rate 100kHz +#define TIMER_NUM 500 +#define TIMER_MODULO 10 + +BReactor sys; + +void handle_timer (BTimer *bt) +{ + #ifdef BADVPN_USE_WINAPI + btime_t time = btime_gettime() + rand()%TIMER_MODULO; + #else + btime_t time = btime_gettime() + random()%TIMER_MODULO; + #endif + BReactor_SetTimerAbsolute(&sys, bt, time); +} + +int main () +{ + BLog_InitStdout(); + + #ifdef BADVPN_USE_WINAPI + srand(time(NULL)); + #else + srandom(time(NULL)); + #endif + + // init time + BTime_Init(); + + if (!BReactor_Init(&sys)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + BTimer timers[TIMER_NUM]; + + int i; + for (i=0; i + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define USE_COUNTS 0 +#define USE_ASSOC 1 + +typedef size_t entry_index; +#define MAX_INDICES SIZE_MAX + +typedef uint32_t entry_key; + +typedef uint8_t assoc_value; +typedef uint64_t assoc_sum; + +struct entry { + entry_index tree_child[2]; + entry_index tree_parent; + int8_t tree_balance; +#if USE_COUNTS + size_t tree_count; +#endif +#if USE_ASSOC + assoc_value assoc_value; + assoc_sum assoc_sum; +#endif + entry_key key; +}; + +typedef struct entry *entry_ptr; + +#include "cavl_test_tree.h" +#include + +#include "cavl_test_tree.h" +#include + +static void random_bytes (char *buf, size_t len) +{ + while (len > 0) { + *((unsigned char *)buf) = rand(); + buf++; + len--; + } +} + +static int uint64_less (void *user, uint64_t a, uint64_t b) +{ + return (a < b); +} + +#if USE_ASSOC +static MyTreeRef assoc_continue_last_lesser_equal (MyTree *tree, struct entry *arg, MyTreeRef ref, assoc_sum target_sum) +{ + assoc_sum cur_sum = MyTree_ExclusiveAssocPrefixSum(tree, arg, ref); + ASSERT(target_sum >= cur_sum) + while (cur_sum + ref.ptr->assoc_value <= target_sum) { + MyTreeRef next_ref = MyTree_GetNext(tree, arg, ref); + if (next_ref.link == -1) { + break; + } + cur_sum += ref.ptr->assoc_value; + ref = next_ref; + } + return ref; +} +#endif + +static void test_assoc (MyTree *tree, struct entry *arg) +{ +#if USE_ASSOC + assoc_sum sum = 0; + for (MyTreeRef ref = MyTree_GetFirst(tree, arg); ref.link != -1; ref = MyTree_GetNext(tree, arg, ref)) { + assoc_sum tree_sum = MyTree_ExclusiveAssocPrefixSum(tree, arg, ref); + ASSERT_FORCE(tree_sum == sum); + ASSERT_FORCE(MyTree_FindLastExclusiveAssocPrefixSumLesserEqual(tree, arg, sum, uint64_less, NULL).link == assoc_continue_last_lesser_equal(tree, arg, ref, sum).link); + ASSERT_FORCE(MyTree_FindLastExclusiveAssocPrefixSumLesserEqual(tree, arg, sum + 1, uint64_less, NULL).link == assoc_continue_last_lesser_equal(tree, arg, ref, sum + 1).link); + sum += ref.ptr->assoc_value; + } + ASSERT_FORCE(sum == MyTree_AssocSum(tree, arg)); +#endif +} + +int main (int argc, char *argv[]) +{ + //srand(time(NULL)); + + printf("sizeof(struct entry)=%" PRIsz "\n", sizeof(struct entry)); + + if (argc != 6) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : "")); + return 1; + } + + size_t num_keys = atoi(argv[1]); + size_t num_lookups = atoi(argv[2]); + size_t num_remove = atoi(argv[3]); + size_t do_remove = atoi(argv[4]); + size_t do_verify = atoi(argv[5]); + + printf("Allocating keys...\n"); + entry_key *keys = (entry_key *)BAllocArray(num_keys, sizeof(keys[0])); + ASSERT_FORCE(keys); + + printf("Generating random keys...\n"); + random_bytes((char *)keys, num_keys * sizeof(keys[0])); + + printf("Allocating lookup indices...\n"); + uint64_t *lookup_indices = (uint64_t *)BAllocArray(num_lookups, sizeof(lookup_indices[0])); + ASSERT_FORCE(lookup_indices); + + printf("Generating random lookup indices...\n"); + random_bytes((char *)lookup_indices, num_lookups * sizeof(lookup_indices[0])); + + printf("Allocating remove indices...\n"); + uint64_t *remove_indices = (uint64_t *)BAllocArray(num_remove, sizeof(remove_indices[0])); + ASSERT_FORCE(remove_indices); + + printf("Generating random remove indices...\n"); + random_bytes((char *)remove_indices, num_remove * sizeof(remove_indices[0])); + +#if USE_ASSOC + printf("Allocating assoc values...\n"); + assoc_value *assoc_values = (assoc_value *)BAllocArray(num_keys, sizeof(assoc_values[0])); + ASSERT_FORCE(assoc_values); + + printf("Generating random assoc values...\n"); + random_bytes((char *)assoc_values, num_keys * sizeof(assoc_values[0])); +#endif + + printf("Allocating entries...\n"); + ASSERT_FORCE(num_keys <= MAX_INDICES); + struct entry *entries = (struct entry *)BAllocArray(num_keys, sizeof(*entries)); + ASSERT_FORCE(entries); + entry_index num_used_entries = 0; + + MyTree tree; + MyTree_Init(&tree); + + struct entry *arg = entries; + + ASSERT_FORCE(MyTree_IsEmpty(&tree)); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == 0); +#endif + test_assoc(&tree, arg); + + size_t num; +#if USE_COUNTS + size_t prevNum; +#endif + + printf("Inserting random numbers...\n"); + num = 0; + for (size_t i = 0; i < num_keys; i++) { + entries[num_used_entries].key = keys[i]; +#if USE_ASSOC + entries[num_used_entries].assoc_value = assoc_values[i]; +#endif + MyTreeRef ref = {&entries[num_used_entries], num_used_entries}; + if (!MyTree_Insert(&tree, arg, ref, NULL)) { + //printf("Insert collision!\n"); + continue; + } + num_used_entries++; + num++; + } + printf("Inserted %" PRIsz ".\n", num); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == num); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + test_assoc(&tree, arg); + } + + printf("Looking up random inserted keys...\n"); + for (size_t i = 0; i < num_lookups; i++) { + entry_index idx = lookup_indices[i] % num_keys; + MyTreeRef entry = MyTree_LookupExact(&tree, arg, keys[idx]); + ASSERT_FORCE(!MyTreeIsNullRef(entry)); + } + +#if USE_COUNTS + prevNum = MyTree_Count(&tree, arg); +#endif + num = 0; + printf("Looking up and removing random inserted keys...\n"); + for (size_t i = 0; i < num_remove; i++) { + entry_index idx = remove_indices[i] % num_keys; + MyTreeRef entry = MyTree_LookupExact(&tree, arg, keys[idx]); + if (MyTreeIsNullRef(entry)) { + //printf("Remove collision!\n"); + continue; + } + ASSERT_FORCE(entry.ptr->key == keys[idx]); + MyTree_Remove(&tree, arg, entry); + num++; + } + printf("Removed %" PRIsz ".\n", num); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == prevNum - num); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + test_assoc(&tree, arg); + } + + if (do_remove) { +#if USE_COUNTS + prevNum = MyTree_Count(&tree, arg); +#endif + num = 0; + printf("Removing remaining...\n"); + + MyTreeRef cur = MyTree_GetFirst(&tree, arg); + while (!MyTreeIsNullRef(cur)) { + MyTreeRef prev = cur; + cur = MyTree_GetNext(&tree, arg, cur); + MyTree_Remove(&tree, arg, prev); + num++; + } + + printf("Removed %" PRIsz ".\n", num); + ASSERT_FORCE(MyTree_IsEmpty(&tree)); +#if USE_COUNTS + ASSERT_FORCE(MyTree_Count(&tree, arg) == 0); + ASSERT_FORCE(num == prevNum); +#endif + if (do_verify) { + printf("Verifying...\n"); + MyTree_Verify(&tree, arg); + } + } + + printf("Freeing...\n"); + BFree(keys); + BFree(lookup_indices); + BFree(remove_indices); +#if USE_ASSOC + BFree(assoc_values); +#endif + BFree(entries); + + return 0; +} diff --git a/external/badvpn_dns/examples/cavl_test_tree.h b/external/badvpn_dns/examples/cavl_test_tree.h new file mode 100644 index 00000000..463076f6 --- /dev/null +++ b/external/badvpn_dns/examples/cavl_test_tree.h @@ -0,0 +1,23 @@ +#define CAVL_PARAM_NAME MyTree +#define CAVL_PARAM_FEATURE_COUNTS USE_COUNTS +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_ASSOC USE_ASSOC +#define CAVL_PARAM_TYPE_ENTRY struct entry +#define CAVL_PARAM_TYPE_LINK entry_index +#define CAVL_PARAM_TYPE_KEY entry_key +#define CAVL_PARAM_TYPE_ARG entry_ptr +#define CAVL_PARAM_TYPE_COUNT size_t +#define CAVL_PARAM_TYPE_ASSOC assoc_sum +#define CAVL_PARAM_VALUE_COUNT_MAX SIZE_MAX +#define CAVL_PARAM_VALUE_NULL ((entry_index)-1) +#define CAVL_PARAM_VALUE_ASSOC_ZERO 0 +#define CAVL_PARAM_FUN_DEREF(arg, link) (&(arg)[(link)]) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1).ptr->key, (entry2).ptr->key) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2).ptr->key) +#define CAVL_PARAM_FUN_ASSOC_VALUE(arg, entry) ((entry).ptr->assoc_value) +#define CAVL_PARAM_FUN_ASSOC_OPER(arg, value1, value2) ((value1) + (value2)) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent +#define CAVL_PARAM_MEMBER_COUNT tree_count +#define CAVL_PARAM_MEMBER_ASSOC assoc_sum diff --git a/external/badvpn_dns/examples/dhcpclient_test.c b/external/badvpn_dns/examples/dhcpclient_test.c new file mode 100644 index 00000000..9601c014 --- /dev/null +++ b/external/badvpn_dns/examples/dhcpclient_test.c @@ -0,0 +1,159 @@ +/** + * @file dhcpclient_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BRandom2 random2; +BDHCPClient dhcp; + +static void signal_handler (void *user); +static void dhcp_handler (void *unused, int event); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + goto fail0; + } + + char *ifname = argv[1]; + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BRandom2_Init(&random2, 0)) { + DEBUG("BRandom2_Init failed"); + goto fail1a; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + struct BDHCPClient_opts opts = {}; + + if (!BDHCPClient_Init(&dhcp, ifname, opts, &reactor, &random2, dhcp_handler, NULL)) { + DEBUG("BDHCPClient_Init failed"); + goto fail3; + } + + BReactor_Exec(&reactor); + + BDHCPClient_Free(&dhcp); +fail3: + BSignal_Finish(); +fail2: + BRandom2_Free(&random2); +fail1a: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 0); +} + +void dhcp_handler (void *unused, int event) +{ + switch (event) { + case BDHCPCLIENT_EVENT_UP: { + printf("DHCP: up"); + + uint32_t ip; + uint8_t *ipb = (void *)&ip; + + BDHCPClient_GetClientIP(&dhcp, &ip); + printf(" IP=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + + BDHCPClient_GetClientMask(&dhcp, &ip); + printf(" Mask=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + + if (BDHCPClient_GetRouter(&dhcp, &ip)) { + printf(" Router=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + } + + uint32_t dns[BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS]; + int num = BDHCPClient_GetDNS(&dhcp, dns, BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS); + for (int i = 0; i < num; i++) { + ip=dns[i]; + printf(" DNS=%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, ipb[0], ipb[1], ipb[2], ipb[3]); + } + + printf("\n"); + } break; + + case BDHCPCLIENT_EVENT_DOWN: { + printf("DHCP: down\n"); + } break; + + case BDHCPCLIENT_EVENT_ERROR: { + printf("DHCP: error\n"); + + // exit reactor + BReactor_Quit(&reactor, 0); + } break; + + default: + ASSERT(0); + } +} diff --git a/external/badvpn_dns/examples/emscripten_test.c b/external/badvpn_dns/examples/emscripten_test.c new file mode 100644 index 00000000..52b03514 --- /dev/null +++ b/external/badvpn_dns/examples/emscripten_test.c @@ -0,0 +1,71 @@ +/** + * @file emscripten_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +BReactor reactor; +BTimer timer; +BPending job; + +static void timer_handler (void *unused) +{ + printf("timer_handler %"PRIu64"\n", btime_gettime()); + + BPending_Set(&job); + BReactor_SetTimer(&reactor, &timer); +} + +static void job_handler (void *unused) +{ + printf("job_handler %"PRIu64"\n", btime_gettime()); +} + +int main () +{ + BTime_Init(); + + BReactor_EmscriptenInit(&reactor); + + BTimer_Init(&timer, 500, timer_handler, NULL); + BReactor_SetTimer(&reactor, &timer); + + BPending_Init(&job, BReactor_PendingGroup(&reactor), job_handler, NULL); + BPending_Set(&job); + + BReactor_EmscriptenSync(&reactor); + return 0; +} diff --git a/external/badvpn_dns/examples/fairqueue_test.c b/external/badvpn_dns/examples/fairqueue_test.c new file mode 100644 index 00000000..482e0860 --- /dev/null +++ b/external/badvpn_dns/examples/fairqueue_test.c @@ -0,0 +1,145 @@ +/** + * @file fairqueue_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define OUTPUT_INTERVAL 0 +#define REMOVE_INTERVAL 1 +#define NUM_INPUTS 3 + +BReactor reactor; +TimerPacketSink sink; +PacketPassFairQueue fq; +PacketPassFairQueueFlow flows[NUM_INPUTS]; +FastPacketSource sources[NUM_INPUTS]; +char *data[] = {"0 data", "1 datadatadata", "2 datadatadatadatadata"}; +BTimer timer; +int current_cancel; + +static void init_input (int i) +{ + PacketPassFairQueueFlow_Init(&flows[i], &fq); + FastPacketSource_Init(&sources[i], PacketPassFairQueueFlow_GetInput(&flows[i]), (uint8_t *)data[i], strlen(data[i]), BReactor_PendingGroup(&reactor)); +} + +static void free_input (int i) +{ + FastPacketSource_Free(&sources[i]); + PacketPassFairQueueFlow_Free(&flows[i]); +} + +static void reset_input (void) +{ + PacketPassFairQueueFlow_AssertFree(&flows[current_cancel]); + + printf("removing %d\n", current_cancel); + + // remove flow + free_input(current_cancel); + + // init flow + init_input(current_cancel); + + // increment cancel + current_cancel = (current_cancel + 1) % NUM_INPUTS; + + // reset timer + BReactor_SetTimer(&reactor, &timer); +} + +static void flow_handler_busy (void *user) +{ + PacketPassFairQueueFlow_AssertFree(&flows[current_cancel]); + + reset_input(); +} + +static void timer_handler (void *user) +{ + // if flow is busy, request cancel and wait for it + if (PacketPassFairQueueFlow_IsBusy(&flows[current_cancel])) { + printf("cancelling %d\n", current_cancel); + PacketPassFairQueueFlow_RequestCancel(&flows[current_cancel]); + PacketPassFairQueueFlow_SetBusyHandler(&flows[current_cancel], flow_handler_busy, NULL); + return; + } + + reset_input(); +} + +int main () +{ + // initialize logging + BLog_InitStdout(); + + // init time + BTime_Init(); + + // initialize reactor + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + // initialize sink + TimerPacketSink_Init(&sink, &reactor, 500, OUTPUT_INTERVAL); + + // initialize queue + if (!PacketPassFairQueue_Init(&fq, TimerPacketSink_GetInput(&sink), BReactor_PendingGroup(&reactor), 1, 1)) { + DEBUG("PacketPassFairQueue_Init failed"); + return 1; + } + + // initialize inputs + for (int i = 0; i < NUM_INPUTS; i++) { + init_input(i); + } + + // init cancel timer + BTimer_Init(&timer, REMOVE_INTERVAL, timer_handler, NULL); + BReactor_SetTimer(&reactor, &timer); + + // init cancel counter + current_cancel = 0; + + // run reactor + int ret = BReactor_Exec(&reactor); + BReactor_Free(&reactor); + return ret; +} diff --git a/external/badvpn_dns/examples/fairqueue_test2.c b/external/badvpn_dns/examples/fairqueue_test2.c new file mode 100644 index 00000000..0fe9d342 --- /dev/null +++ b/external/badvpn_dns/examples/fairqueue_test2.c @@ -0,0 +1,93 @@ +/** + * @file fairqueue_test2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SINK_TIMER 0 + +int main () +{ + // initialize logging + BLog_InitStdout(); + + // init time + BTime_Init(); + + // initialize reactor + BReactor reactor; + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + return 1; + } + + // initialize sink + RandomPacketSink sink; + RandomPacketSink_Init(&sink, &reactor, 500, SINK_TIMER); + + // initialize queue + PacketPassFairQueue fq; + if (!PacketPassFairQueue_Init(&fq, RandomPacketSink_GetInput(&sink), BReactor_PendingGroup(&reactor), 0, 1)) { + DEBUG("PacketPassFairQueue_Init failed"); + return 1; + } + + // initialize source 1 + PacketPassFairQueueFlow flow1; + PacketPassFairQueueFlow_Init(&flow1, &fq); + FastPacketSource source1; + char data1[] = "data1"; + FastPacketSource_Init(&source1, PacketPassFairQueueFlow_GetInput(&flow1), (uint8_t *)data1, strlen(data1), BReactor_PendingGroup(&reactor)); + + // initialize source 2 + PacketPassFairQueueFlow flow2; + PacketPassFairQueueFlow_Init(&flow2, &fq); + FastPacketSource source2; + char data2[] = "data2data2"; + FastPacketSource_Init(&source2, PacketPassFairQueueFlow_GetInput(&flow2), (uint8_t *)data2, strlen(data2), BReactor_PendingGroup(&reactor)); + + // initialize source 3 + PacketPassFairQueueFlow flow3; + PacketPassFairQueueFlow_Init(&flow3, &fq); + FastPacketSource source3; + char data3[] = "data3data3data3data3data3data3data3data3data3"; + FastPacketSource_Init(&source3, PacketPassFairQueueFlow_GetInput(&flow3), (uint8_t *)data3, strlen(data3), BReactor_PendingGroup(&reactor)); + + // run reactor + int ret = BReactor_Exec(&reactor); + BReactor_Free(&reactor); + return ret; +} diff --git a/external/badvpn_dns/examples/indexedlist_test.c b/external/badvpn_dns/examples/indexedlist_test.c new file mode 100644 index 00000000..d5282c06 --- /dev/null +++ b/external/badvpn_dns/examples/indexedlist_test.c @@ -0,0 +1,95 @@ +/** + * @file indexedlist_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +IndexedList il; + +struct elem { + int value; + IndexedListNode node; +}; + +static void elem_insert (struct elem *e, int value, uint64_t index) +{ + e->value = value; + IndexedList_InsertAt(&il, &e->node, index); +} + +static void remove_at (uint64_t index) +{ + IndexedListNode *n = IndexedList_GetAt(&il, index); + struct elem *e = UPPER_OBJECT(n, struct elem, node); + IndexedList_Remove(&il, &e->node); +} + +static void print_list (void) +{ + for (uint64_t i = 0; i < IndexedList_Count(&il); i++) { + IndexedListNode *n = IndexedList_GetAt(&il, i); + struct elem *e = UPPER_OBJECT(n, struct elem, node); + printf("%d ", e->value); + } + printf("\n"); +} + +int main (int argc, char *argv[]) +{ + IndexedList_Init(&il); + + struct elem arr[100]; + + print_list(); + + elem_insert(&arr[0], 1, 0); + print_list(); + elem_insert(&arr[1], 2, 0); + print_list(); + elem_insert(&arr[2], 3, 0); + print_list(); + elem_insert(&arr[3], 4, 0); + print_list(); + elem_insert(&arr[4], 5, 0); + print_list(); + elem_insert(&arr[5], 6, 0); + print_list(); + + elem_insert(&arr[6], 7, 1); + print_list(); + + remove_at(0); + print_list(); + + remove_at(5); + print_list(); + + return 0; +} diff --git a/external/badvpn_dns/examples/ipaddr6_test.c b/external/badvpn_dns/examples/ipaddr6_test.c new file mode 100644 index 00000000..7da486d9 --- /dev/null +++ b/external/badvpn_dns/examples/ipaddr6_test.c @@ -0,0 +1,169 @@ +/** + * @file ipaddr6_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include + +#define PRINT_TEST(addr_bytes, string) \ + { \ + struct ipv6_addr addr = {addr_bytes}; \ + char str[IPADDR6_PRINT_MAX]; \ + ipaddr6_print_addr(addr, str); \ + ASSERT_FORCE(!strcmp(str, (string))); \ + struct ipv6_addr parsed_addr; \ + int res = ipaddr6_parse_ipv6_addr_bin(str, strlen(str), &parsed_addr); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(!memcmp(parsed_addr.bytes, addr.bytes, 16)); \ + } + +#define PARSE_TEST(string, addr_bytes) \ + { \ + struct ipv6_addr exp_addr = {addr_bytes}; \ + struct ipv6_addr addr; \ + int res = ipaddr6_parse_ipv6_addr_bin((string), strlen((string)), &addr); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(!memcmp(addr.bytes, exp_addr.bytes, 16)); \ + } + +#define PARSE_FAIL_TEST(string) \ + { \ + struct ipv6_addr addr; \ + int res = ipaddr6_parse_ipv6_addr_bin((string), strlen((string)), &addr); \ + ASSERT_FORCE(!res); \ + } + +#define MASK_TEST(mask_bytes, prefix) \ + { \ + struct ipv6_addr mask = {mask_bytes}; \ + int parsed_prefix; \ + int res = ipaddr6_ipv6_prefix_from_mask(mask, &parsed_prefix); \ + ASSERT_FORCE(res); \ + ASSERT_FORCE(parsed_prefix == (prefix)); \ + struct ipv6_addr generated_mask; \ + ipaddr6_ipv6_mask_from_prefix(parsed_prefix, &generated_mask); \ + ASSERT_FORCE(!memcmp(generated_mask.bytes, mask.bytes, 16)); \ + } + +#define PASS(...) __VA_ARGS__ + +int main () +{ + PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "::1") + PRINT_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), "::") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}),"2001:db8::1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01}), "2001:db8::2:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01}), "2001:db8:0:1:1:1:1:1") + PRINT_TEST(PASS({0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}), "2001:db8::1:0:0:1") + + PARSE_TEST("::", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("::1", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) + PARSE_TEST("::abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0xcd})) + PARSE_TEST("::0123:abcd", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x23, 0xab, 0xcd})) + PARSE_TEST("abcd::", PASS({0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("abcd:0123::", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) + PARSE_TEST("1::2", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})) + PARSE_TEST("abcd:0123::3210:dcba", PASS({0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("4567:abcd:0123::3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210::", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0x00, 0x00})) + PARSE_TEST("::4567:abcd:0123:1111:2222:3333:3210", PASS({0x00, 0x00, 0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10})) + PARSE_TEST("4567:abcd:0123:1111:2222:3333:3210:dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("04567:000abcd:00000123:01111:2222:03333:0003210:0dcba", PASS({0x45, 0x67, 0xab, 0xcd, 0x01, 0x23, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x32, 0x10, 0xdc, 0xba})) + PARSE_TEST("::1.2.3.4", PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ff::1.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ff::0.2.3.4", PASS({0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04})) + PARSE_TEST("1:2:3:4:5:6:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", PASS({0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) + PARSE_TEST("1::fffa:1.2.3.4", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x01, 0x02, 0x03, 0x04})) + PARSE_TEST("1::fffa:0.0.0.0", PASS({0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00})) + + PARSE_FAIL_TEST("") + PARSE_FAIL_TEST(":") + PARSE_FAIL_TEST("a") + PARSE_FAIL_TEST("a:b") + PARSE_FAIL_TEST(":b") + PARSE_FAIL_TEST("b:") + PARSE_FAIL_TEST("1:2:3:4:5:6:7") + PARSE_FAIL_TEST(":1:2:3:4:5:6:7") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:") + PARSE_FAIL_TEST(":::") + PARSE_FAIL_TEST("::a::") + PARSE_FAIL_TEST("::a::b") + PARSE_FAIL_TEST("c::a::b") + PARSE_FAIL_TEST("c::a::") + PARSE_FAIL_TEST("10000::") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9") + PARSE_FAIL_TEST("1:2:3:4::5:6:7:8:9") + PARSE_FAIL_TEST("::1:2:3:4:5:6:7:8:9") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:8:9::") + PARSE_FAIL_TEST("a::b:") + PARSE_FAIL_TEST(":a::b") + PARSE_FAIL_TEST("::g") + PARSE_FAIL_TEST("::1.2") + PARSE_FAIL_TEST("::1.2.3.4.5") + PARSE_FAIL_TEST("::01.2.3.4") + PARSE_FAIL_TEST("::1.2.3.04") + PARSE_FAIL_TEST("::1.2.3.256") + PARSE_FAIL_TEST("1.2.3.4") + PARSE_FAIL_TEST("::8259.2.473.256") + PARSE_FAIL_TEST("1:2:3:4:5:6:7:1.2.3.4") + PARSE_FAIL_TEST("1:2:3:4:5:1.2.3.4") + PARSE_FAIL_TEST("::1.2.3.4::") + PARSE_FAIL_TEST("::1.2.3.4:1") + PARSE_FAIL_TEST("localhost6") + + MASK_TEST(PASS({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 0) + MASK_TEST(PASS({0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 1) + MASK_TEST(PASS({0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 2) + MASK_TEST(PASS({0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 3) + MASK_TEST(PASS({0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 4) + MASK_TEST(PASS({0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 5) + MASK_TEST(PASS({0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 6) + MASK_TEST(PASS({0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 7) + MASK_TEST(PASS({0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 8) + + MASK_TEST(PASS({0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 9) + MASK_TEST(PASS({0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 10) + MASK_TEST(PASS({0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 11) + MASK_TEST(PASS({0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 12) + MASK_TEST(PASS({0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 13) + MASK_TEST(PASS({0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 14) + MASK_TEST(PASS({0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 15) + MASK_TEST(PASS({0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), 16) + + MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE}), 127) + MASK_TEST(PASS({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}), 128) + + return 0; +} diff --git a/external/badvpn_dns/examples/ncd_parser_test.c b/external/badvpn_dns/examples/ncd_parser_test.c new file mode 100644 index 00000000..ac913b54 --- /dev/null +++ b/external/badvpn_dns/examples/ncd_parser_test.c @@ -0,0 +1,294 @@ +/** + * @file ncd_parser_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int generate_val (NCDValue *value, ExpString *out_str) +{ + switch (NCDValue_Type(value)) { + case NCDVALUE_STRING: { + const char *str = NCDValue_StringValue(value); + size_t len = NCDValue_StringLength(value); + + if (!ExpString_AppendChar(out_str, '"')) { + goto fail; + } + + for (size_t i = 0; i < len; i++) { + if (str[i] == '\0') { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02"PRIx8, (uint8_t)str[i]); + + if (!ExpString_Append(out_str, buf)) { + goto fail; + } + + continue; + } + + if (str[i] == '"' || str[i] == '\\') { + if (!ExpString_AppendChar(out_str, '\\')) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, str[i])) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, '"')) { + goto fail; + } + } break; + + case NCDVALUE_LIST: { + if (!ExpString_AppendChar(out_str, '{')) { + goto fail; + } + + int is_first = 1; + + for (NCDValue *e = NCDValue_ListFirst(value); e; e = NCDValue_ListNext(value, e)) { + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + goto fail; + } + } + + if (!generate_val(e, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, '}')) { + goto fail; + } + } break; + + case NCDVALUE_MAP: { + if (!ExpString_AppendChar(out_str, '[')) { + goto fail; + } + + int is_first = 1; + + for (NCDValue *ekey = NCDValue_MapFirstKey(value); ekey; ekey = NCDValue_MapNextKey(value, ekey)) { + NCDValue *eval = NCDValue_MapKeyValue(value, ekey); + + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + goto fail; + } + } + + if (!generate_val(ekey, out_str)) { + goto fail; + } + + if (!ExpString_AppendChar(out_str, ':')) { + goto fail; + } + + if (!generate_val(eval, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, ']')) { + goto fail; + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static void print_indent (unsigned int indent) +{ + while (indent > 0) { + printf(" "); + indent--; + } +} + +static void print_value (NCDValue *v, unsigned int indent) +{ + ExpString estr; + if (!ExpString_Init(&estr)) { + DEBUG("ExpString_Init failed"); + exit(1); + } + + if (!generate_val(v, &estr)) { + DEBUG("generate_val failed"); + exit(1); + } + + print_indent(indent); + printf("%s\n", ExpString_Get(&estr)); + + ExpString_Free(&estr); +} + +static void print_block (NCDBlock *block, unsigned int indent) +{ + for (NCDStatement *st = NCDBlock_FirstStatement(block); st; st = NCDBlock_NextStatement(block, st)) { + const char *name = NCDStatement_Name(st) ? NCDStatement_Name(st) : ""; + + switch (NCDStatement_Type(st)) { + case NCDSTATEMENT_REG: { + const char *objname = NCDStatement_RegObjName(st) ? NCDStatement_RegObjName(st) : ""; + const char *cmdname = NCDStatement_RegCmdName(st); + + print_indent(indent); + printf("reg name=%s objname=%s cmdname=%s args:\n", name, objname, cmdname); + + print_value(NCDStatement_RegArgs(st), indent + 2); + } break; + + case NCDSTATEMENT_IF: { + print_indent(indent); + printf("if name=%s\n", name); + + NCDIfBlock *ifb = NCDStatement_IfBlock(st); + + for (NCDIf *ifc = NCDIfBlock_FirstIf(ifb); ifc; ifc = NCDIfBlock_NextIf(ifb, ifc)) { + print_indent(indent + 2); + printf("if\n"); + + print_value(NCDIf_Cond(ifc), indent + 4); + + print_indent(indent + 2); + printf("then\n"); + + print_block(NCDIf_Block(ifc), indent + 4); + } + + if (NCDStatement_IfElse(st)) { + print_indent(indent + 2); + printf("else\n"); + + print_block(NCDStatement_IfElse(st), indent + 4); + } + } break; + + case NCDSTATEMENT_FOREACH: { + const char *name1 = NCDStatement_ForeachName1(st); + const char *name2 = NCDStatement_ForeachName2(st) ? NCDStatement_ForeachName2(st) : ""; + + print_indent(indent); + printf("foreach name=%s name1=%s name2=%s\n", name, name1, name2); + + print_block(NCDStatement_ForeachBlock(st), indent + 2); + } break; + + default: ASSERT(0); + } + } +} + +int main (int argc, char **argv) +{ + int res = 1; + + if (argc != 3) { + printf("Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + int desugar = atoi(argv[1]); + char *text = argv[2]; + + BLog_InitStdout(); + + // parse + NCDProgram prog; + if (!NCDConfigParser_Parse(text, strlen(text), &prog)) { + DEBUG("NCDConfigParser_Parse failed"); + goto fail1; + } + + // desugar + if (desugar) { + if (!NCDSugar_Desugar(&prog)) { + DEBUG("NCDSugar_Desugar failed"); + goto fail2; + } + } + + // print + for (NCDProgramElem *elem = NCDProgram_FirstElem(&prog); elem; elem = NCDProgram_NextElem(&prog, elem)) { + switch (NCDProgramElem_Type(elem)) { + case NCDPROGRAMELEM_PROCESS: { + NCDProcess *p = NCDProgramElem_Process(elem); + printf("process name=%s is_template=%d\n", NCDProcess_Name(p), NCDProcess_IsTemplate(p)); + print_block(NCDProcess_Block(p), 2); + } break; + + case NCDPROGRAMELEM_INCLUDE: { + printf("include path=%s\n", NCDProgramElem_IncludePathData(elem)); + } break; + + case NCDPROGRAMELEM_INCLUDE_GUARD: { + printf("include_guard id=%s\n", NCDProgramElem_IncludeGuardIdData(elem)); + } break; + + default: ASSERT(0); + } + } + + res = 0; +fail2: + NCDProgram_Free(&prog); +fail1: + BLog_Free(); +fail0: + return res; +} diff --git a/external/badvpn_dns/examples/ncd_tokenizer_test.c b/external/badvpn_dns/examples/ncd_tokenizer_test.c new file mode 100644 index 00000000..84f90eb8 --- /dev/null +++ b/external/badvpn_dns/examples/ncd_tokenizer_test.c @@ -0,0 +1,149 @@ +/** + * @file ncd_tokenizer_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +int error; + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + if (token == NCD_ERROR) { + printf("line %zu, character %zu: tokenizer error\n", line, line_char); + error = 1; + return 0; + } + + switch (token) { + case NCD_EOF: + printf("eof\n"); + break; + case NCD_TOKEN_CURLY_OPEN: + printf("curly_open\n"); + break; + case NCD_TOKEN_CURLY_CLOSE: + printf("curly_close\n"); + break; + case NCD_TOKEN_ROUND_OPEN: + printf("round_open\n"); + break; + case NCD_TOKEN_ROUND_CLOSE: + printf("round_close\n"); + break; + case NCD_TOKEN_SEMICOLON: + printf("semicolon\n"); + break; + case NCD_TOKEN_DOT: + printf("dot\n"); + break; + case NCD_TOKEN_COMMA: + printf("comma\n"); + break; + case NCD_TOKEN_PROCESS: + printf("process\n"); + break; + case NCD_TOKEN_NAME: + printf("name %s\n", value); + free(value); + break; + case NCD_TOKEN_STRING: + printf("string %s\n", value); + free(value); + break; + case NCD_TOKEN_ARROW: + printf("arrow\n"); + break; + case NCD_TOKEN_TEMPLATE: + printf("template\n"); + break; + case NCD_TOKEN_COLON: + printf("colon\n"); + break; + case NCD_TOKEN_BRACKET_OPEN: + printf("bracket open\n"); + break; + case NCD_TOKEN_BRACKET_CLOSE: + printf("bracket close\n"); + break; + case NCD_TOKEN_IF: + printf("if\n"); + break; + case NCD_TOKEN_ELIF: + printf("elif\n"); + break; + case NCD_TOKEN_ELSE: + printf("else\n"); + break; + case NCD_TOKEN_FOREACH: + printf("foreach\n"); + break; + case NCD_TOKEN_AS: + printf("as\n"); + break; + case NCD_TOKEN_INCLUDE: + printf("include\n"); + break; + case NCD_TOKEN_INCLUDE_GUARD: + printf("include_guard\n"); + break; + default: + ASSERT(0); + } + + return 1; +} + +int main (int argc, char **argv) +{ + if (argc < 1) { + return 1; + } + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + BLog_InitStdout(); + + error = 0; + + NCDConfigTokenizer_Tokenize(argv[1], strlen(argv[1]), tokenizer_output, NULL); + + if (error) { + return 1; + } + + return 0; +} diff --git a/external/badvpn_dns/examples/ncd_value_parser_test.c b/external/badvpn_dns/examples/ncd_value_parser_test.c new file mode 100644 index 00000000..cf1915dd --- /dev/null +++ b/external/badvpn_dns/examples/ncd_value_parser_test.c @@ -0,0 +1,78 @@ +/** + * @file ncd_value_parser_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ + int res = 1; + + if (argc != 2) { + printf("Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + BLog_InitStdout(); + + NCDValMem mem; + NCDValMem_Init(&mem); + + // parse + NCDValRef val; + if (!NCDValParser_Parse(argv[1], strlen(argv[1]), &mem, &val)) { + DEBUG("NCDValParser_Parse failed"); + goto fail1; + } + + // generate value string + char *str = NCDValGenerator_Generate(val); + if (!str) { + DEBUG("NCDValGenerator_Generate failed"); + goto fail1; + } + + // print value string + printf("%s\n", str); + + res = 0; + + free(str); +fail1: + NCDValMem_Free(&mem); + BLog_Free(); +fail0: + return res; +} diff --git a/external/badvpn_dns/examples/ncdinterfacemonitor_test.c b/external/badvpn_dns/examples/ncdinterfacemonitor_test.c new file mode 100644 index 00000000..167f1bd3 --- /dev/null +++ b/external/badvpn_dns/examples/ncdinterfacemonitor_test.c @@ -0,0 +1,150 @@ +/** + * @file ncdinterfacemonitor_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +NCDInterfaceMonitor monitor; + +static void signal_handler (void *user); +static void monitor_handler (void *unused, struct NCDInterfaceMonitor_event event); +static void monitor_handler_error (void *unused); + +int main (int argc, char **argv) +{ + int ret = 1; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + int ifindex; + if (!badvpn_get_iface_info(argv[1], NULL, NULL, &ifindex)) { + DEBUG("get_iface_info failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + int watch_flags = NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR; + + if (!NCDInterfaceMonitor_Init(&monitor, ifindex, watch_flags, &reactor, NULL, monitor_handler, monitor_handler_error)) { + DEBUG("NCDInterfaceMonitor_Init failed"); + goto fail3; + } + + ret = BReactor_Exec(&reactor); + + NCDInterfaceMonitor_Free(&monitor); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 1); +} + +void monitor_handler (void *unused, struct NCDInterfaceMonitor_event event) +{ + switch (event.event) { + case NCDIFMONITOR_EVENT_LINK_UP: + case NCDIFMONITOR_EVENT_LINK_DOWN: { + const char *type = (event.event == NCDIFMONITOR_EVENT_LINK_UP) ? "up" : "down"; + printf("link %s\n", type); + } break; + + case NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED: + case NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED: { + const char *type = (event.event == NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED) ? "added" : "removed"; + uint8_t *addr = (uint8_t *)&event.u.ipv4_addr.addr; + printf("ipv4 addr %s %d.%d.%d.%d/%d\n", type, (int)addr[0], (int)addr[1], (int)addr[2], (int)addr[3], event.u.ipv4_addr.addr.prefix); + } break; + + case NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED: + case NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED: { + const char *type = (event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED) ? "added" : "removed"; + + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(event.u.ipv6_addr.addr.addr, str); + + int dynamic = !!(event.u.ipv6_addr.addr_flags & NCDIFMONITOR_ADDR_FLAG_DYNAMIC); + + printf("ipv6 addr %s %s/%d scope=%"PRIu8" dynamic=%d\n", type, str, event.u.ipv6_addr.addr.prefix, event.u.ipv6_addr.scope, dynamic); + } break; + + default: ASSERT(0); + } +} + +void monitor_handler_error (void *unused) +{ + DEBUG("monitor error"); + + BReactor_Quit(&reactor, 1); +} diff --git a/external/badvpn_dns/examples/ncdudevmanager_test.c b/external/badvpn_dns/examples/ncdudevmanager_test.c new file mode 100644 index 00000000..9bbb3fe3 --- /dev/null +++ b/external/badvpn_dns/examples/ncdudevmanager_test.c @@ -0,0 +1,161 @@ +/** + * @file ncdudevmanager_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BUnixSignal usignal; +BProcessManager manager; +NCDUdevManager umanager; +NCDUdevClient client; + +static void signal_handler (void *user, int signo); +static void client_handler (void *unused, char *devpath, int have_map, BStringMap map); + +int main (int argc, char **argv) +{ + if (!(argc == 1 || (argc == 2 && !strcmp(argv[1], "--no-udev")))) { + fprintf(stderr, "Usage: %s [--no-udev]\n", (argc > 0 ? argv[0] : NULL)); + goto fail0; + } + + int no_udev = (argc == 2); + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + if (!BUnixSignal_Init(&usignal, &reactor, set, signal_handler, NULL)) { + fprintf(stderr, "BUnixSignal_Init failed\n"); + goto fail2; + } + + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + NCDUdevManager_Init(&umanager, no_udev, &reactor, &manager); + + NCDUdevClient_Init(&client, &umanager, NULL, client_handler); + + BReactor_Exec(&reactor); + + NCDUdevClient_Free(&client); + + NCDUdevManager_Free(&umanager); + + BProcessManager_Free(&manager); +fail3: + BUnixSignal_Free(&usignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +static void signal_handler (void *user, int signo) +{ + if (signo == SIGHUP) { + fprintf(stderr, "received SIGHUP, restarting client\n"); + + NCDUdevClient_Free(&client); + NCDUdevClient_Init(&client, &umanager, NULL, client_handler); + } else { + fprintf(stderr, "received %s, exiting\n", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // exit event loop + BReactor_Quit(&reactor, 1); + } +} + +void client_handler (void *unused, char *devpath, int have_map, BStringMap map) +{ + printf("event %s\n", devpath); + + if (!have_map) { + printf(" no map\n"); + } else { + printf(" map:\n"); + + const char *name = BStringMap_First(&map); + while (name) { + printf(" %s=%s\n", name, BStringMap_Get(&map, name)); + name = BStringMap_Next(&map, name); + } + } + + const BStringMap *cache_map = NCDUdevManager_Query(&umanager, devpath); + if (!cache_map) { + printf(" no cache\n"); + } else { + printf(" cache:\n"); + + const char *name = BStringMap_First(cache_map); + while (name) { + printf(" %s=%s\n", name, BStringMap_Get(cache_map, name)); + name = BStringMap_Next(cache_map, name); + } + } + + if (have_map) { + BStringMap_Free(&map); + } + free(devpath); +} diff --git a/external/badvpn_dns/examples/ncdudevmonitor_test.c b/external/badvpn_dns/examples/ncdudevmonitor_test.c new file mode 100644 index 00000000..94b4f6f4 --- /dev/null +++ b/external/badvpn_dns/examples/ncdudevmonitor_test.c @@ -0,0 +1,152 @@ +/** + * @file ncdudevmonitor_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +BReactor reactor; +BProcessManager manager; +NCDUdevMonitor monitor; + +static void signal_handler (void *user); +static void monitor_handler_event (void *unused); +static void monitor_handler_error (void *unused, int is_error); + +int main (int argc, char **argv) +{ + int ret = 1; + + if (argc < 2 || (strcmp(argv[1], "monitor_udev") && strcmp(argv[1], "monitor_kernel") && strcmp(argv[1], "info"))) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + goto fail0; + } + + int mode; + if (!strcmp(argv[1], "monitor_udev")) { + mode = NCDUDEVMONITOR_MODE_MONITOR_UDEV; + } else if (!strcmp(argv[1], "monitor_kernel")) { + mode = NCDUDEVMONITOR_MODE_MONITOR_KERNEL; + } else { + mode = NCDUDEVMONITOR_MODE_INFO; + } + + if (!BNetwork_GlobalInit()) { + DEBUG("BNetwork_GlobalInit failed"); + goto fail0; + } + + BTime_Init(); + + BLog_InitStdout(); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + DEBUG("BSignal_Init failed"); + goto fail2; + } + + if (!BProcessManager_Init(&manager, &reactor)) { + DEBUG("BProcessManager_Init failed"); + goto fail3; + } + + if (!NCDUdevMonitor_Init(&monitor, &reactor, &manager, mode, NULL, + monitor_handler_event, + monitor_handler_error + )) { + DEBUG("NCDUdevMonitor_Init failed"); + goto fail4; + } + + ret = BReactor_Exec(&reactor); + + NCDUdevMonitor_Free(&monitor); +fail4: + BProcessManager_Free(&manager); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return ret; +} + +void signal_handler (void *user) +{ + DEBUG("termination requested"); + + BReactor_Quit(&reactor, 1); +} + +void monitor_handler_event (void *unused) +{ + // accept event + NCDUdevMonitor_Done(&monitor); + + if (NCDUdevMonitor_IsReadyEvent(&monitor)) { + printf("ready\n"); + return; + } + + printf("event\n"); + + int num_props = NCDUdevMonitor_GetNumProperties(&monitor); + for (int i = 0; i < num_props; i++) { + const char *name; + const char *value; + NCDUdevMonitor_GetProperty(&monitor, i, &name, &value); + printf(" %s=%s\n", name, value); + } +} + +void monitor_handler_error (void *unused, int is_error) +{ + if (is_error) { + DEBUG("monitor error"); + } else { + DEBUG("monitor finished"); + } + + BReactor_Quit(&reactor, (is_error ? 1 : 0)); +} diff --git a/external/badvpn_dns/examples/ncdval_test.c b/external/badvpn_dns/examples/ncdval_test.c new file mode 100644 index 00000000..6933ed0a --- /dev/null +++ b/external/badvpn_dns/examples/ncdval_test.c @@ -0,0 +1,380 @@ +/** + * @file ncdval_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define FORCE(cmd) if (!(cmd)) { fprintf(stderr, "failed\n"); exit(1); } + +struct composed_string { + BRefTarget ref_target; + size_t length; + size_t chunk_size; + char **chunks; +}; + +static void composed_string_ref_target_func_release (BRefTarget *ref_target) +{ + struct composed_string *cs = UPPER_OBJECT(ref_target, struct composed_string, ref_target); + + size_t num_chunks = cs->length / cs->chunk_size; + if (cs->length % cs->chunk_size) { + num_chunks++; + } + + for (size_t i = 0; i < num_chunks; i++) { + BFree(cs->chunks[i]); + } + + BFree(cs->chunks); + BFree(cs); +} + +static void composed_string_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length) +{ + struct composed_string *cs = user; + ASSERT(offset < cs->length) + + *out_data = cs->chunks[offset / cs->chunk_size] + (offset % cs->chunk_size); + *out_length = cs->chunk_size - (offset % cs->chunk_size); +} + +static NCDValRef build_composed_string (NCDValMem *mem, const char *data, size_t length, size_t chunk_size) +{ + ASSERT(chunk_size > 0) + + struct composed_string *cs = BAlloc(sizeof(*cs)); + if (!cs) { + goto fail0; + } + + cs->length = length; + cs->chunk_size = chunk_size; + + size_t num_chunks = cs->length / cs->chunk_size; + if (cs->length % cs->chunk_size) { + num_chunks++; + } + + cs->chunks = BAllocArray(num_chunks, sizeof(cs->chunks[0])); + if (!cs->chunk_size) { + goto fail1; + } + + size_t i; + for (i = 0; i < num_chunks; i++) { + cs->chunks[i] = BAlloc(cs->chunk_size); + if (!cs->chunks[i]) { + goto fail2; + } + + size_t to_copy = length; + if (to_copy > cs->chunk_size) { + to_copy = cs->chunk_size; + } + + memcpy(cs->chunks[i], data, to_copy); + data += to_copy; + length -= to_copy; + } + + BRefTarget_Init(&cs->ref_target, composed_string_ref_target_func_release); + + NCDValComposedStringResource resource; + resource.func_getptr = composed_string_func_getptr; + resource.user = cs; + resource.ref_target = &cs->ref_target; + + NCDValRef val = NCDVal_NewComposedString(mem, resource, 0, cs->length); + BRefTarget_Deref(&cs->ref_target); + return val; + +fail2: + while (i-- > 0) { + BFree(cs->chunks[i]); + } + BFree(cs->chunks); +fail1: + BFree(cs); +fail0: + return NCDVal_NewInvalid(); +} + +static void test_string (NCDValRef str, const char *data, size_t length) +{ + FORCE( !NCDVal_IsInvalid(str) ) + FORCE( NCDVal_IsString(str) ) + FORCE( NCDVal_StringLength(str) == length ) + FORCE( NCDVal_StringHasNulls(str) == !!memchr(data, '\0', length) ) + FORCE( NCDVal_IsStringNoNulls(str) == !memchr(data, '\0', length) ) + FORCE( NCDVal_StringRegionEquals(str, 0, length, data) ) + + b_cstring cstr = NCDVal_StringCstring(str); + + for (size_t i = 0; i < length; i++) { + size_t chunk_length; + const char *chunk_data = b_cstring_get(cstr, i, length - i, &chunk_length); + + FORCE( chunk_length > 0 ) + FORCE( chunk_length <= length - i ) + FORCE( !memcmp(chunk_data, data + i, chunk_length) ) + FORCE( NCDVal_StringRegionEquals(str, i, chunk_length, data + i) ) + FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data, length), i, i, chunk_length) == 0 ) + FORCE( b_cstring_memcmp(cstr, b_cstring_make_buf(data + i, length - i), i, 0, chunk_length) == 0 ) + } +} + +static void print_indent (int indent) +{ + for (int i = 0; i < indent; i++) { + printf(" "); + } +} + +static void print_value (NCDValRef val, unsigned int indent) +{ + switch (NCDVal_Type(val)) { + case NCDVAL_STRING: { + NCDValNullTermString nts; + FORCE( NCDVal_StringNullTerminate(val, &nts) ) + + print_indent(indent); + printf("string(%zu) %s\n", NCDVal_StringLength(val), nts.data); + + NCDValNullTermString_Free(&nts); + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(val); + + print_indent(indent); + printf("list(%zu)\n", NCDVal_ListCount(val)); + + for (size_t i = 0; i < count; i++) { + NCDValRef elem_val = NCDVal_ListGet(val, i); + print_value(elem_val, indent + 1); + } + } break; + + case NCDVAL_MAP: { + print_indent(indent); + printf("map(%zu)\n", NCDVal_MapCount(val)); + + for (NCDValMapElem e = NCDVal_MapOrderedFirst(val); !NCDVal_MapElemInvalid(e); e = NCDVal_MapOrderedNext(val, e)) { + NCDValRef ekey = NCDVal_MapElemKey(val, e); + NCDValRef eval = NCDVal_MapElemVal(val, e); + + print_indent(indent + 1); + printf("key=\n"); + print_value(ekey, indent + 2); + + print_indent(indent + 1); + printf("val=\n"); + print_value(eval, indent + 2); + } + } break; + } +} + +int main () +{ + int res; + + BLog_InitStdout(); + + NCDStringIndex string_index; + FORCE( NCDStringIndex_Init(&string_index) ) + + // Some basic usage of values. + + NCDValMem mem; + NCDValMem_Init(&mem); + + NCDValRef s1 = NCDVal_NewString(&mem, "Hello World"); + test_string(s1, "Hello World", 11); + ASSERT( NCDVal_IsString(s1) ) + ASSERT( !NCDVal_IsIdString(s1) ) + ASSERT( NCDVal_Type(s1) == NCDVAL_STRING ) + + NCDValRef s2 = NCDVal_NewString(&mem, "This is reeeeeeeeeeeeeallllllllyyyyy fun!"); + FORCE( !NCDVal_IsInvalid(s2) ) + + NCDValRef l1 = NCDVal_NewList(&mem, 10); + FORCE( !NCDVal_IsInvalid(l1) ) + + FORCE( NCDVal_ListAppend(l1, s1) ) + FORCE( NCDVal_ListAppend(l1, s2) ) + + print_value(s1, 0); + print_value(s2, 0); + print_value(l1, 0); + + NCDValRef k1 = NCDVal_NewString(&mem, "K1"); + FORCE( !NCDVal_IsInvalid(k1) ) + NCDValRef v1 = NCDVal_NewString(&mem, "V1"); + FORCE( !NCDVal_IsInvalid(v1) ) + + NCDValRef k2 = NCDVal_NewString(&mem, "K2"); + FORCE( !NCDVal_IsInvalid(k2) ) + NCDValRef v2 = NCDVal_NewString(&mem, "V2"); + FORCE( !NCDVal_IsInvalid(v2) ) + + NCDValRef m1 = NCDVal_NewMap(&mem, 3); + FORCE( !NCDVal_IsInvalid(m1) ) + + FORCE( NCDVal_MapInsert(m1, k1, v1, &res) && res ) + FORCE( NCDVal_MapInsert(m1, k2, v2, &res) && res ) + + ASSERT( NCDVal_MapGetValue(m1, "K1").idx == v1.idx ) + ASSERT( NCDVal_MapGetValue(m1, "K2").idx == v2.idx ) + ASSERT( NCDVal_IsInvalid(NCDVal_MapGetValue(m1, "K3")) ) + + NCDValRef ids1 = NCDVal_NewIdString(&mem, NCD_STRING_ARG1, &string_index); + test_string(ids1, "_arg1", 5); + ASSERT( !memcmp(NCDVal_StringData(ids1), "_arg1", 5) ) + ASSERT( NCDVal_StringLength(ids1) == 5 ) + ASSERT( !NCDVal_StringHasNulls(ids1) ) + ASSERT( NCDVal_StringEquals(ids1, "_arg1") ) + ASSERT( NCDVal_Type(ids1) == NCDVAL_STRING ) + ASSERT( NCDVal_IsIdString(ids1) ) + + NCDValRef ids2 = NCDVal_NewIdString(&mem, NCD_STRING_ARG2, &string_index); + test_string(ids2, "_arg2", 5); + ASSERT( !memcmp(NCDVal_StringData(ids2), "_arg2", 5) ) + ASSERT( NCDVal_StringLength(ids2) == 5 ) + ASSERT( !NCDVal_StringHasNulls(ids2) ) + ASSERT( NCDVal_StringEquals(ids2, "_arg2") ) + ASSERT( NCDVal_Type(ids2) == NCDVAL_STRING ) + ASSERT( NCDVal_IsIdString(ids2) ) + + FORCE( NCDVal_MapInsert(m1, ids1, ids2, &res) && res ) + + ASSERT( NCDVal_MapGetValue(m1, "_arg1").idx == ids2.idx ) + + print_value(m1, 0); + + NCDValRef copy = NCDVal_NewCopy(&mem, m1); + FORCE( !NCDVal_IsInvalid(copy) ) + ASSERT( NCDVal_Compare(copy, m1) == 0 ) + + NCDValMem_Free(&mem); + + // Try to make copies of a string within the same memory object. + // This is an evil test because we cannot simply copy a string using e.g. + // NCDVal_NewStringBin() - it requires that the buffer passed + // be outside the memory object of the new string. + // We use NCDVal_NewCopy(), which takes care of this by creating + // an uninitialized string using NCDVal_NewStringUninitialized() and + // then copyng the data. + + NCDValMem_Init(&mem); + + NCDValRef s[100]; + + s[0] = NCDVal_NewString(&mem, "Eeeeeeeeeeeevil."); + FORCE( !NCDVal_IsInvalid(s[0]) ) + + for (int i = 1; i < 100; i++) { + s[i] = NCDVal_NewCopy(&mem, s[i - 1]); + FORCE( !NCDVal_IsInvalid(s[i]) ) + ASSERT( NCDVal_StringEquals(s[i - 1], "Eeeeeeeeeeeevil.") ) + ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") ) + } + + for (int i = 0; i < 100; i++) { + ASSERT( NCDVal_StringEquals(s[i], "Eeeeeeeeeeeevil.") ) + } + + NCDValMem_Free(&mem); + + NCDValMem_Init(&mem); + + NCDValRef cstr1 = build_composed_string(&mem, "Hello World", 11, 3); + test_string(cstr1, "Hello World", 11); + FORCE( NCDVal_IsComposedString(cstr1) ) + FORCE( !NCDVal_IsContinuousString(cstr1) ) + FORCE( NCDVal_StringEquals(cstr1, "Hello World") ) + FORCE( !NCDVal_StringEquals(cstr1, "Hello World ") ) + FORCE( !NCDVal_StringEquals(cstr1, "Hello WorlD") ) + + NCDValRef cstr2 = build_composed_string(&mem, "GoodBye", 7, 1); + test_string(cstr2, "GoodBye", 7); + FORCE( NCDVal_IsComposedString(cstr2) ) + FORCE( !NCDVal_IsContinuousString(cstr2) ) + FORCE( NCDVal_StringEquals(cstr2, "GoodBye") ) + FORCE( !NCDVal_StringEquals(cstr2, " GoodBye") ) + FORCE( !NCDVal_StringEquals(cstr2, "goodBye") ) + + NCDValRef cstr3 = build_composed_string(&mem, "Bad\x00String", 10, 4); + test_string(cstr3, "Bad\x00String", 10); + FORCE( NCDVal_IsComposedString(cstr3) ) + FORCE( !NCDVal_IsContinuousString(cstr3) ) + + FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 1, 2, 3) < 0 ) + FORCE( NCDVal_StringMemCmp(cstr1, cstr2, 7, 1, 4) > 0 ) + + char buf[10]; + NCDVal_StringCopyOut(cstr1, 1, 10, buf); + FORCE( !memcmp(buf, "ello World", 10) ) + + NCDValRef clist1 = NCDVal_NewList(&mem, 3); + FORCE( !NCDVal_IsInvalid(clist1) ) + FORCE( NCDVal_ListAppend(clist1, cstr1) ) + FORCE( NCDVal_ListAppend(clist1, cstr2) ) + FORCE( NCDVal_ListAppend(clist1, cstr3) ) + FORCE( NCDVal_ListCount(clist1) == 3 ) + + FORCE( NCDValMem_ConvertNonContinuousStrings(&mem, &clist1) ) + FORCE( NCDVal_ListCount(clist1) == 3 ) + + NCDValRef fixed_str1 = NCDVal_ListGet(clist1, 0); + NCDValRef fixed_str2 = NCDVal_ListGet(clist1, 1); + NCDValRef fixed_str3 = NCDVal_ListGet(clist1, 2); + + FORCE( NCDVal_IsContinuousString(fixed_str1) ) + FORCE( NCDVal_IsContinuousString(fixed_str2) ) + FORCE( NCDVal_IsContinuousString(fixed_str3) ) + + test_string(fixed_str1, "Hello World", 11); + test_string(fixed_str2, "GoodBye", 7); + test_string(fixed_str3, "Bad\x00String", 10); + + NCDValMem_Free(&mem); + + NCDStringIndex_Free(&string_index); + + return 0; +} diff --git a/external/badvpn_dns/examples/ncdvalcons_test.c b/external/badvpn_dns/examples/ncdvalcons_test.c new file mode 100644 index 00000000..7a876ed1 --- /dev/null +++ b/external/badvpn_dns/examples/ncdvalcons_test.c @@ -0,0 +1,111 @@ +/** + * @file ncdvalcons_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +static NCDValMem mem; +static NCDValCons cons; + +static NCDValConsVal make_string (const char *data) +{ + NCDValConsVal val; + int error; + int res = NCDValCons_NewString(&cons, (const uint8_t *)data, strlen(data), &val, &error); + ASSERT_FORCE(res) + return val; +} + +static NCDValConsVal make_list (void) +{ + NCDValConsVal val; + NCDValCons_NewList(&cons, &val); + return val; +} + +static NCDValConsVal make_map (void) +{ + NCDValConsVal val; + NCDValCons_NewMap(&cons, &val); + return val; +} + +static NCDValConsVal list_prepend (NCDValConsVal list, NCDValConsVal elem) +{ + int error; + int res = NCDValCons_ListPrepend(&cons, &list, elem, &error); + ASSERT_FORCE(res) + return list; +} + +static NCDValConsVal map_insert (NCDValConsVal map, NCDValConsVal key, NCDValConsVal value) +{ + int error; + int res = NCDValCons_MapInsert(&cons, &map, key, value, &error); + ASSERT_FORCE(res) + return map; +} + +static NCDValRef complete (NCDValConsVal cval) +{ + int error; + NCDValRef val; + int res = NCDValCons_Complete(&cons, cval, &val, &error); + ASSERT_FORCE(res) + return val; +} + +int main () +{ + NCDValMem_Init(&mem); + + int res = NCDValCons_Init(&cons, &mem); + ASSERT_FORCE(res) + + NCDValRef val1 = complete(list_prepend(list_prepend(list_prepend(make_list(), make_string("hello")), make_string("world")), make_list())); + char *str1 = NCDValGenerator_Generate(val1); + ASSERT_FORCE(str1) + ASSERT_FORCE(!strcmp(str1, "{{}, \"world\", \"hello\"}")) + free(str1); + + NCDValRef val2 = complete(map_insert(map_insert(map_insert(make_map(), make_list(), make_list()), make_string("A"), make_list()), make_string("B"), make_list())); + char *str2 = NCDValGenerator_Generate(val2); + ASSERT_FORCE(str2) + printf("%s\n", str2); + ASSERT_FORCE(!strcmp(str2, "[\"A\":{}, \"B\":{}, {}:{}]")) + free(str2); + + NCDValCons_Free(&cons); + NCDValMem_Free(&mem); + return 0; +} diff --git a/external/badvpn_dns/examples/parse_number_test.c b/external/badvpn_dns/examples/parse_number_test.c new file mode 100644 index 00000000..d393c3f3 --- /dev/null +++ b/external/badvpn_dns/examples/parse_number_test.c @@ -0,0 +1,130 @@ +/** + * @file parse_number_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static void test_random (int num_digits, int digit_modulo) +{ + ASSERT(num_digits > 0); + ASSERT(digit_modulo > 0); + + uint8_t digits[40]; + + for (int i = 0; i < num_digits; i++) { + digits[i] = '0' + (rand() % digit_modulo); + } + digits[num_digits] = '\0'; + + char *endptr; + uintmax_t std_num = strtoumax((const char *)digits, &endptr, 10); + int std_res = !*endptr && !(std_num == UINTMAX_MAX && errno == ERANGE); + + uintmax_t num = 0; + int res = parse_unsigned_integer_bin((const char *)digits, num_digits, &num); + + if (res != std_res) { + printf("fail1 %s\n", (const char *)digits); + ASSERT_FORCE(0); + } + + if (res && num != std_num) { + printf("fail2 %s\n", (const char *)digits); + ASSERT_FORCE(0); + } + + if (res) { + uint8_t *nozero_digits = digits; + while (*nozero_digits == '0' && nozero_digits != &digits[num_digits - 1]) { + nozero_digits++; + } + + char buf[40]; + int size = compute_decimal_repr_size(num); + generate_decimal_repr(num, buf, size); + buf[size] = '\0'; + ASSERT_FORCE(!strcmp(buf, (const char *)nozero_digits)); + } +} + +static void test_value (uintmax_t x) +{ + char str[40]; + sprintf(str, "%" PRIuMAX, x); + uintmax_t y; + int res = parse_unsigned_integer_bin(str, strlen(str), &y); + ASSERT_FORCE(res); + ASSERT_FORCE(y == x); + + char str2[40]; + int size = compute_decimal_repr_size(x); + generate_decimal_repr(x, str2, size); + str2[size] = '\0'; + + ASSERT_FORCE(!strcmp(str2, str)); +} + +static void test_value_range (uintmax_t start, uintmax_t count) +{ + uintmax_t i = start; + do { + test_value(i); + i++; + } while (i != start + count); +} + +int main () +{ + srand(time(NULL)); + + for (int num_digits = 1; num_digits <= 22; num_digits++) { + for (int i = 0; i < 1000000; i++) { + test_random(num_digits, 10); + } + for (int i = 0; i < 1000000; i++) { + test_random(num_digits, 11); + } + } + + test_value_range(UINTMAX_C(0), 5000000); + test_value_range(UINTMAX_C(100000000), 5000000); + test_value_range(UINTMAX_C(258239003), 5000000); + test_value_range(UINTMAX_C(8241096180752634), 5000000); + test_value_range(UINTMAX_C(9127982390882308083), 5000000); + test_value_range(UINTMAX_C(18446744073700000000), 20000000); + + return 0; +} diff --git a/external/badvpn_dns/examples/predicate_test.c b/external/badvpn_dns/examples/predicate_test.c new file mode 100644 index 00000000..324a9603 --- /dev/null +++ b/external/badvpn_dns/examples/predicate_test.c @@ -0,0 +1,116 @@ +/** + * @file predicate_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +static int func_hello (void *user, void **args) +{ + return 1; +} + +static int func_neg (void *user, void **args) +{ + int arg = *((int *)args[0]); + + return !arg; +} + +static int func_conj (void *user, void **args) +{ + int arg1 = *((int *)args[0]); + int arg2 = *((int *)args[1]); + + return (arg1 && arg2); +} + +static int func_strcmp (void *user, void **args) +{ + char *arg1 = (char *)args[0]; + char *arg2 = (char *)args[1]; + + return (!strcmp(arg1, arg2)); +} + +static int func_error (void *user, void **args) +{ + return -1; +} + +int main (int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + // init logger + BLog_InitStdout(); + + // init predicate + BPredicate pr; + if (!BPredicate_Init(&pr, argv[1])) { + fprintf(stderr, "BPredicate_Init failed\n"); + return 1; + } + + // init functions + BPredicateFunction f_hello; + BPredicateFunction_Init(&f_hello, &pr, "hello", NULL, 0, func_hello, NULL); + int arr1[] = {PREDICATE_TYPE_BOOL}; + BPredicateFunction f_neg; + BPredicateFunction_Init(&f_neg, &pr, "neg", arr1, 1, func_neg, NULL); + int arr2[] = {PREDICATE_TYPE_BOOL, PREDICATE_TYPE_BOOL}; + BPredicateFunction f_conj; + BPredicateFunction_Init(&f_conj, &pr, "conj", arr2, 2, func_conj, NULL); + int arr3[] = {PREDICATE_TYPE_STRING, PREDICATE_TYPE_STRING}; + BPredicateFunction f_strcmp; + BPredicateFunction_Init(&f_strcmp, &pr, "strcmp", arr3, 2, func_strcmp, NULL); + BPredicateFunction f_error; + BPredicateFunction_Init(&f_error, &pr, "error", NULL, 0, func_error, NULL); + + // evaluate + int result = BPredicate_Eval(&pr); + printf("%d\n", result); + + // free functions + BPredicateFunction_Free(&f_hello); + BPredicateFunction_Free(&f_neg); + BPredicateFunction_Free(&f_conj); + BPredicateFunction_Free(&f_strcmp); + BPredicateFunction_Free(&f_error); + + // free predicate + BPredicate_Free(&pr); + + return 0; +} diff --git a/external/badvpn_dns/examples/savl_test.c b/external/badvpn_dns/examples/savl_test.c new file mode 100644 index 00000000..18cf1912 --- /dev/null +++ b/external/badvpn_dns/examples/savl_test.c @@ -0,0 +1,135 @@ +/** + * @file savl_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +struct mynode; + +#include "savl_test_tree.h" +#include + +struct mynode { + int used; + int num; + MyTreeNode tree_node; +}; + +#include "savl_test_tree.h" +#include + +static void verify (MyTree *tree) +{ + printf("Verifying...\n"); + MyTree_Verify(tree, 0); +} + +int main (int argc, char **argv) +{ + int num_nodes; + int num_random_delete; + + if (argc != 3 || (num_nodes = atoi(argv[1])) <= 0 || (num_random_delete = atoi(argv[2])) < 0) { + fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : NULL)); + return 1; + } + + struct mynode *nodes = (struct mynode *)BAllocArray(num_nodes, sizeof(*nodes)); + ASSERT_FORCE(nodes) + + int *values_ins = (int *)BAllocArray(num_nodes, sizeof(int)); + ASSERT_FORCE(values_ins) + + int *values = (int *)BAllocArray(num_random_delete, sizeof(int)); + ASSERT_FORCE(values) + + MyTree tree; + MyTree_Init(&tree); + verify(&tree); + + printf("Inserting random values...\n"); + int inserted = 0; + BRandom_randomize((uint8_t *)values_ins, num_nodes * sizeof(int)); + for (int i = 0; i < num_nodes; i++) { + nodes[i].num = values_ins[i]; + if (MyTree_Insert(&tree, 0, &nodes[i], NULL)) { + nodes[i].used = 1; + inserted++; + } else { + nodes[i].used = 0; + printf("Insert collision!\n"); + } + } + printf("Inserted %d entries\n", inserted); + ASSERT_FORCE(MyTree_Count(&tree, 0) == inserted) + verify(&tree); + + printf("Removing random entries...\n"); + int removed1 = 0; + BRandom_randomize((uint8_t *)values, num_random_delete * sizeof(int)); + for (int i = 0; i < num_random_delete; i++) { + int index = (((unsigned int *)values)[i] % num_nodes); + struct mynode *node = nodes + index; + if (node->used) { + MyTree_Remove(&tree, 0, node); + node->used = 0; + removed1++; + } + } + printf("Removed %d entries\n", removed1); + ASSERT_FORCE(MyTree_Count(&tree, 0) == inserted - removed1) + verify(&tree); + + printf("Removing remaining...\n"); + int removed2 = 0; + while (!MyTree_IsEmpty(&tree)) { + struct mynode *node = MyTree_GetFirst(&tree, 0); + ASSERT_FORCE(node->used) + MyTree_Remove(&tree, 0, node); + node->used = 0; + removed2++; + } + printf("Removed %d entries\n", removed2); + ASSERT_FORCE(MyTree_IsEmpty(&tree)) + ASSERT_FORCE(removed1 + removed2 == inserted) + ASSERT_FORCE(MyTree_Count(&tree, 0) == 0) + verify(&tree); + + BFree(nodes); + BFree(values_ins); + BFree(values); + + return 0; +} diff --git a/external/badvpn_dns/examples/savl_test_tree.h b/external/badvpn_dns/examples/savl_test_tree.h new file mode 100644 index 00000000..41964e96 --- /dev/null +++ b/external/badvpn_dns/examples/savl_test_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME MyTree +#define SAVL_PARAM_FEATURE_COUNTS 1 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct mynode +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_TYPE_COUNT int +#define SAVL_PARAM_VALUE_COUNT_MAX INT_MAX +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->num, (entry2)->num) +#define SAVL_PARAM_MEMBER_NODE tree_node diff --git a/external/badvpn_dns/examples/stdin_input.c b/external/badvpn_dns/examples/stdin_input.c new file mode 100644 index 00000000..8ff752c8 --- /dev/null +++ b/external/badvpn_dns/examples/stdin_input.c @@ -0,0 +1,138 @@ +/** + * @file stdin_input.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Example program which reads stdin and waits for SIGINT and SIGTERM. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#define BUF_SIZE 64 + +BReactor reactor; +BConnection pipe_con; +BUnixSignal usignal; +StreamRecvInterface *source_if; +uint8_t buf[BUF_SIZE + 1]; + +static void signal_handler (void *user, int signo) +{ + fprintf(stderr, "received %s, exiting\n", (signo == SIGINT ? "SIGINT" : "SIGTERM")); + + // exit event loop + BReactor_Quit(&reactor, 1); +} + +static void connection_handler (void *user, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + fprintf(stderr, "pipe closed\n"); + } else { + fprintf(stderr, "pipe error\n"); + } + + // exit event loop + BReactor_Quit(&reactor, (event == BCONNECTION_EVENT_RECVCLOSED ? 0 : 1)); +} + +static void input_handler_done (void *user, int data_len) +{ + // receive next chunk + StreamRecvInterface_Receiver_Recv(source_if, buf, BUF_SIZE); + + // print this chunk + buf[data_len] = '\0'; + printf("Received: '%s'\n", buf); +} + +int main () +{ + int ret = 1; + + BLog_InitStdout(); + + // init network + if (!BNetwork_GlobalInit()) { + fprintf(stderr, "BNetwork_GlobalInit failed\n"); + goto fail1; + } + + // init reactor (event loop) + if (!BReactor_Init(&reactor)) { + fprintf(stderr, "BReactor_Init failed\n"); + goto fail1; + } + + // init signal handling + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + if (!BUnixSignal_Init(&usignal, &reactor, set, signal_handler, NULL)) { + fprintf(stderr, "BUnixSignal_Init failed\n"); + goto fail2; + } + + // init BConnection object backed by the stdin fd + if (!BConnection_Init(&pipe_con, BConnection_source_pipe(0), &reactor, NULL, connection_handler)) { + fprintf(stderr, "BConnection_Init failed\n"); + goto fail3; + } + + // init connection receive interface + BConnection_RecvAsync_Init(&pipe_con); + source_if = BConnection_RecvAsync_GetIf(&pipe_con); + + // init receive done callback + StreamRecvInterface_Receiver_Init(source_if, input_handler_done, NULL); + + // receive first chunk + StreamRecvInterface_Receiver_Recv(source_if, buf, BUF_SIZE); + + // run event loop + ret = BReactor_Exec(&reactor); + + BConnection_RecvAsync_Free(&pipe_con); + BConnection_Free(&pipe_con); +fail3: + BUnixSignal_Free(&usignal, 0); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); + DebugObjectGlobal_Finish(); + return ret; +} diff --git a/external/badvpn_dns/examples/substring_test.c b/external/badvpn_dns/examples/substring_test.c new file mode 100644 index 00000000..4a4b6c85 --- /dev/null +++ b/external/badvpn_dns/examples/substring_test.c @@ -0,0 +1,204 @@ +/** + * @file substring_test.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +static int find_substring_slow (const char *str, size_t str_len, const char *sub, size_t sub_len, size_t *out_pos) +{ + ASSERT(sub_len > 0) + + if (str_len < sub_len) { + return 0; + } + + for (size_t i = 0; i <= str_len - sub_len; i++) { + if (!memcmp(str + i, sub, sub_len)) { + *out_pos = i; + return 1; + } + } + + return 0; +} + +static void print_data (const char *str, size_t str_len) +{ + while (str_len > 0) { + printf("%02"PRIx8" ", (uint8_t)(*str)); + str++; + str_len--; + } + printf("\n"); +} + +static void print_table (const size_t *table, size_t len) +{ + for (size_t i = 1; i < len; i++) { + printf("%zu ", table[i]); + } + printf("\n"); +} + +static void test_tables (int len, int count) +{ + ASSERT(len > 0) + ASSERT(count >= 0) + + char *word = (char *)BAllocSize(bsize_fromint(len)); + ASSERT_FORCE(word) + + size_t *table = (size_t *)BAllocSize(bsize_mul(bsize_fromint(len), bsize_fromsize(sizeof(table[0])))); + ASSERT_FORCE(table) + + for (int i = 0; i < count; i++) { + for (int j = 0; j < len; j++) { + word[j] = rand() % 2; + } + + build_substring_backtrack_table(word, len, table); + + for (int j = 1; j < len; j++) { + for (int k = j - 1; k >= 0; k--) { + if (!memcmp(word + j - k, word, k)) { + ASSERT_FORCE(table[j] == k) + break; + } + } + } + } + + BFree(table); + BFree(word); +} + +static void test_substring (int word_len, int text_len, int word_count, int text_count) +{ + assert(word_len > 0); + assert(text_len >= 0); + assert(word_count >= 0); + assert(text_count >= 0); + + char *word = (char *)BAllocSize(bsize_fromint(word_len)); + ASSERT_FORCE(word) + + size_t *table = (size_t *)BAllocSize(bsize_mul(bsize_fromint(word_len), bsize_fromsize(sizeof(table[0])))); + ASSERT_FORCE(table) + + char *text = (char *)BAllocSize(bsize_fromint(text_len)); + ASSERT_FORCE(text) + + for (int i = 0; i < word_count; i++) { + for (int j = 0; j < word_len; j++) { + word[j] = rand() % 2; + } + + build_substring_backtrack_table(word, word_len, table); + + for (int j = 0; j < text_count; j++) { + for (int k = 0; k < text_len; k++) { + text[k] = rand() % 2; + } + + size_t pos = 36; // to remove warning + int res = find_substring(text, text_len, word, word_len, table, &pos); + + size_t spos = 59; // to remove warning + int sres = find_substring_slow(text, text_len, word, word_len, &spos); + + ASSERT_FORCE(res == sres) + if (res) { + ASSERT_FORCE(pos == spos) + } + } + } + + BFree(text); + BFree(table); + BFree(word); +} + +int main (int argc, char *argv[]) +{ + if (argc != 7) { + printf("Usage: %s \n", (argc == 0 ? "" : argv[0])); + return 1; + } + + int tables_len = atoi(argv[1]); + int tables_count = atoi(argv[2]); + int word_len = atoi(argv[3]); + int text_len = atoi(argv[4]); + int word_count = atoi(argv[5]); + int text_count = atoi(argv[6]); + + if (tables_len <= 0 || tables_count < 0 || word_len <= 0 || text_len < 0 || word_count < 0 || text_count < 0) { + printf("Bad arguments.\n"); + return 1; + } + + srand(time(NULL)); + + test_tables(tables_len, tables_count); + + test_substring(word_len, text_len, word_count, text_count); + + { + char text[] = "aggagaa"; + char word[] = "aga"; + size_t table[sizeof(word) - 1]; + build_substring_backtrack_table(word, strlen(word), table); + + size_t pos; + int res = find_substring(text, strlen(text), word, strlen(word), table, &pos); + ASSERT_FORCE(res) + ASSERT_FORCE(pos == 3) + } + + { + char text[] = "aagagga"; + char word[] = "aga"; + size_t table[sizeof(word) - 1]; + build_substring_backtrack_table_reverse(word, strlen(word), table); + + size_t pos; + int res = find_substring_reverse(text, strlen(text), word, strlen(word), table, &pos); + ASSERT_FORCE(res) + ASSERT_FORCE(pos == 1) + } + + return 0; +} diff --git a/external/badvpn_dns/fix_flex.php b/external/badvpn_dns/fix_flex.php new file mode 100644 index 00000000..bd026d0b --- /dev/null +++ b/external/badvpn_dns/fix_flex.php @@ -0,0 +1,10 @@ +", "#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L"); +$replace = array("", "#if 1"); +$contents = str_replace($search, $replace, $contents); +$res = file_put_contents($filename, $contents); +if ($res === FALSE) exit(1); diff --git a/external/badvpn_dns/flooder/CMakeLists.txt b/external/badvpn_dns/flooder/CMakeLists.txt new file mode 100644 index 00000000..36253ab7 --- /dev/null +++ b/external/badvpn_dns/flooder/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(badvpn-flooder flooder.c) +target_link_libraries(badvpn-flooder system flow server_conection ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-flooder + RUNTIME DESTINATION bin +) diff --git a/external/badvpn_dns/flooder/flooder.c b/external/badvpn_dns/flooder/flooder.c new file mode 100644 index 00000000..1f3f05cc --- /dev/null +++ b/external/badvpn_dns/flooder/flooder.c @@ -0,0 +1,671 @@ +/** + * @file flooder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int ssl; + char *nssdb; + char *client_cert_name; + char *server_name; + char *server_addr; + peerid_t floods[MAX_FLOODS]; + int num_floods; +} options; + +// server address we connect to +BAddr server_addr; + +// server name to use for SSL +char server_name[256]; + +// reactor +BReactor ss; + +// client certificate if using SSL +CERTCertificate *client_cert; + +// client private key if using SSL +SECKEYPrivateKey *client_key; + +// server connection +ServerConnection server; + +// whether server is ready +int server_ready; + +// my ID, defined only after server_ready +peerid_t my_id; + +// flooding output +PacketRecvInterface flood_source; +PacketProtoEncoder flood_encoder; +SinglePacketBuffer flood_buffer; + +// whether we were asked for a packet and blocked +int flood_blocking; + +// index of next peer to send packet too +int flood_next; + +/** + * Cleans up everything that can be cleaned up from inside the event loop. + */ +static void terminate (void); + +/** + * Prints command line help. + */ +static void print_help (const char *name); + +/** + * Prints program name, version and copyright notice. + */ +static void print_version (void); + +/** + * Parses command line options into the options strucute. + * + * @return 1 on success, 0 on failure + */ +static int parse_arguments (int argc, char *argv[]); + +/** + * Processes command line options. + * + * @return 1 on success, 0 on failure + */ +static int resolve_arguments (void); + +/** + * Handler invoked when program termination is requested. + */ +static void signal_handler (void *unused); + +static void server_handler_error (void *user); +static void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip); +static void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); +static void server_handler_endclient (void *user, peerid_t peer_id); +static void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +static void flood_source_handler_recv (void *user, uint8_t *data); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // resolve addresses + if (!resolve_arguments()) { + BLog(BLOG_ERROR, "Failed to resolve arguments"); + goto fail1; + } + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail1a; + } + + if (options.ssl) { + // init NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // register local NSPR file types + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail3; + } + + // init NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail2; + } + + // set cipher policy + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail3; + } + + // init server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail3; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.client_cert_name, &client_cert, &client_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail4; + } + } + + // start connecting to server + if (!ServerConnection_Init( + &server, &ss, NULL, server_addr, SC_KEEPALIVE_INTERVAL, SERVER_BUFFER_MIN_PACKETS, options.ssl, 0, client_cert, client_key, server_name, NULL, + server_handler_error, server_handler_ready, server_handler_newclient, server_handler_endclient, server_handler_message + )) { + BLog(BLOG_ERROR, "ServerConnection_Init failed"); + goto fail5; + } + + // set server not ready + server_ready = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + if (server_ready) { + ServerConnection_ReleaseBuffers(&server); + SinglePacketBuffer_Free(&flood_buffer); + PacketProtoEncoder_Free(&flood_encoder); + PacketRecvInterface_Free(&flood_source); + } + + ServerConnection_Free(&server); +fail5: + if (options.ssl) { + CERT_DestroyCertificate(client_cert); + SECKEY_DestroyPrivateKey(client_key); +fail4: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail3: + SSL_ClearSessionCache(); + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail2: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + + BSignal_Finish(); +fail1a: + BReactor_Free(&ss); +fail1: + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void terminate (void) +{ + BLog(BLOG_NOTICE, "tearing down"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--ssl --nssdb --client-cert-name ]\n" + " [--server-name ]\n" + " --server-addr \n" + " [--flood-id ] ...\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.ssl = 0; + options.nssdb = NULL; + options.client_cert_name = NULL; + options.server_name = NULL; + options.server_addr = NULL; + options.num_floods = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.client_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--flood-id")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_floods == MAX_FLOODS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.floods[options.num_floods] = atoi(argv[i + 1]); + options.num_floods++; + i++; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.ssl != !!options.nssdb) { + fprintf(stderr, "False: --ssl <=> --nssdb\n"); + return 0; + } + + if (options.ssl != !!options.client_cert_name) { + fprintf(stderr, "False: --ssl <=> --client-cert-name\n"); + return 0; + } + + if (!options.server_addr) { + fprintf(stderr, "False: --server-addr\n"); + return 0; + } + + return 1; +} + +int resolve_arguments (void) +{ + // resolve server address + ASSERT(options.server_addr) + if (!BAddr_Parse(&server_addr, options.server_addr, server_name, sizeof(server_name))) { + BLog(BLOG_ERROR, "server addr: BAddr_Parse failed"); + return 0; + } + if (!addr_supported(server_addr)) { + BLog(BLOG_ERROR, "server addr: not supported"); + return 0; + } + + // override server name if requested + if (options.server_name) { + if (strlen(options.server_name) >= sizeof(server_name)) { + BLog(BLOG_ERROR, "server name: too long"); + return 0; + } + strcpy(server_name, options.server_name); + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +void server_handler_error (void *user) +{ + BLog(BLOG_ERROR, "server connection failed, exiting"); + + terminate(); +} + +void server_handler_ready (void *user, peerid_t param_my_id, uint32_t ext_ip) +{ + ASSERT(!server_ready) + + // remember our ID + my_id = param_my_id; + + // init flooding + + // init source + PacketRecvInterface_Init(&flood_source, SC_MAX_ENC, flood_source_handler_recv, NULL, BReactor_PendingGroup(&ss)); + + // init encoder + PacketProtoEncoder_Init(&flood_encoder, &flood_source, BReactor_PendingGroup(&ss)); + + // init buffer + if (!SinglePacketBuffer_Init(&flood_buffer, PacketProtoEncoder_GetOutput(&flood_encoder), ServerConnection_GetSendInterface(&server), BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed, exiting"); + goto fail1; + } + + // set not blocking + flood_blocking = 0; + + // set server ready + server_ready = 1; + + BLog(BLOG_INFO, "server: ready, my ID is %d", (int)my_id); + + return; + +fail1: + PacketProtoEncoder_Free(&flood_encoder); + PacketRecvInterface_Free(&flood_source); + terminate(); +} + +void server_handler_newclient (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len) +{ + ASSERT(server_ready) + + BLog(BLOG_INFO, "newclient %d", (int)peer_id); +} + +void server_handler_endclient (void *user, peerid_t peer_id) +{ + ASSERT(server_ready) + + BLog(BLOG_INFO, "endclient %d", (int)peer_id); +} + +void server_handler_message (void *user, peerid_t peer_id, uint8_t *data, int data_len) +{ + ASSERT(server_ready) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_MSGLEN) + + BLog(BLOG_INFO, "message from %d", (int)peer_id); +} + +void flood_source_handler_recv (void *user, uint8_t *data) +{ + ASSERT(server_ready) + ASSERT(!flood_blocking) + if (options.num_floods > 0) { + ASSERT(flood_next >= 0) + ASSERT(flood_next < options.num_floods) + } + + if (options.num_floods == 0) { + flood_blocking = 1; + return; + } + + peerid_t peer_id = options.floods[flood_next]; + flood_next = (flood_next + 1) % options.num_floods; + + BLog(BLOG_INFO, "message to %d", (int)peer_id); + + struct sc_header header; + header.type = SCID_OUTMSG; + memcpy(data, &header, sizeof(header)); + + struct sc_client_outmsg omsg; + omsg.clientid = htol16(peer_id); + memcpy(data + sizeof(header), &omsg, sizeof(omsg)); + + memset(data + sizeof(struct sc_header) + sizeof(struct sc_client_outmsg), 0, SC_MAX_MSGLEN); + + PacketRecvInterface_Done(&flood_source, sizeof(struct sc_header) + sizeof(struct sc_client_outmsg) + SC_MAX_MSGLEN); +} diff --git a/external/badvpn_dns/flooder/flooder.h b/external/badvpn_dns/flooder/flooder.h new file mode 100644 index 00000000..c8b84431 --- /dev/null +++ b/external/badvpn_dns/flooder/flooder.h @@ -0,0 +1,37 @@ +/** + * @file flooder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "flooder" + +// server output buffer size +#define SERVER_BUFFER_MIN_PACKETS 200 + +// maximum number of peers to flood +#define MAX_FLOODS 64 diff --git a/external/badvpn_dns/flow/BufferWriter.c b/external/badvpn_dns/flow/BufferWriter.c new file mode 100644 index 00000000..b0e41296 --- /dev/null +++ b/external/badvpn_dns/flow/BufferWriter.c @@ -0,0 +1,112 @@ +/** + * @file BufferWriter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void output_handler_recv (BufferWriter *o, uint8_t *data) +{ + ASSERT(!o->out_have) + + // set output packet + o->out_have = 1; + o->out = data; +} + +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init output + PacketRecvInterface_Init(&o->recv_interface, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no output packet + o->out_have = 0; + + DebugObject_Init(&o->d_obj); + #ifndef NDEBUG + o->d_mtu = mtu; + o->d_writing = 0; + #endif +} + +void BufferWriter_Free (BufferWriter *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->recv_interface); +} + +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->recv_interface; +} + +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) +{ + ASSERT(!o->d_writing) + DebugObject_Access(&o->d_obj); + + if (!o->out_have) { + return 0; + } + + if (buf) { + *buf = o->out; + } + + #ifndef NDEBUG + o->d_writing = 1; + #endif + + return 1; +} + +void BufferWriter_EndPacket (BufferWriter *o, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->d_mtu) + ASSERT(o->out_have) + ASSERT(o->d_writing) + DebugObject_Access(&o->d_obj); + + // set no output packet + o->out_have = 0; + + // finish packet + PacketRecvInterface_Done(&o->recv_interface, len); + + #ifndef NDEBUG + o->d_writing = 0; + #endif +} diff --git a/external/badvpn_dns/flow/BufferWriter.h b/external/badvpn_dns/flow/BufferWriter.h new file mode 100644 index 00000000..6b6a9c44 --- /dev/null +++ b/external/badvpn_dns/flow/BufferWriter.h @@ -0,0 +1,107 @@ +/** + * @file BufferWriter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ + +#ifndef BADVPN_FLOW_BUFFERWRITER_H +#define BADVPN_FLOW_BUFFERWRITER_H + +#include + +#include +#include +#include + +/** + * Object for writing packets to a {@link PacketRecvInterface} client + * in a best-effort fashion. + */ +typedef struct { + PacketRecvInterface recv_interface; + int out_have; + uint8_t *out; + DebugObject d_obj; + #ifndef NDEBUG + int d_mtu; + int d_writing; + #endif +} BufferWriter; + +/** + * Initializes the object. + * The object is initialized in not writing state. + * + * @param o the object + * @param mtu maximum input packet length + * @param pg pending group + */ +void BufferWriter_Init (BufferWriter *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void BufferWriter_Free (BufferWriter *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BufferWriter_GetOutput (BufferWriter *o); + +/** + * Attempts to provide a memory location for writing a packet. + * The object must be in not writing state. + * On success, the object enters writing state. + * + * @param o the object + * @param buf if not NULL, on success, the memory location will be stored here. + * It will have space for MTU bytes. + * @return 1 on success, 0 on failure + */ +int BufferWriter_StartPacket (BufferWriter *o, uint8_t **buf) WARN_UNUSED; + +/** + * Submits a packet written to the buffer. + * The object must be in writing state. + * Yhe object enters not writing state. + * + * @param o the object + * @param len length of the packet that was written. Must be >=0 and + * <=MTU. + */ +void BufferWriter_EndPacket (BufferWriter *o, int len); + +#endif diff --git a/external/badvpn_dns/flow/CMakeLists.txt b/external/badvpn_dns/flow/CMakeLists.txt new file mode 100644 index 00000000..6cd82f6c --- /dev/null +++ b/external/badvpn_dns/flow/CMakeLists.txt @@ -0,0 +1,31 @@ +set(FLOW_SOURCES + PacketPassFairQueue.c + PacketPassPriorityQueue.c + PacketPassConnector.c + PacketRecvConnector.c + StreamRecvConnector.c + PacketRecvBlocker.c + PacketPassNotifier.c + PacketBuffer.c + SinglePacketBuffer.c + PacketCopier.c + PacketStreamSender.c + PacketProtoEncoder.c + PacketProtoDecoder.c + PacketProtoFlow.c + SinglePacketSender.c + BufferWriter.c + PacketPassInterface.c + PacketRecvInterface.c + StreamPassInterface.c + StreamRecvInterface.c + RouteBuffer.c + PacketRouter.c + LineBuffer.c + SingleStreamSender.c + SingleStreamReceiver.c + StreamPacketSender.c + StreamPassConnector.c + PacketPassFifoQueue.c +) +badvpn_add_library(flow "base" "" "${FLOW_SOURCES}") diff --git a/external/badvpn_dns/flow/LineBuffer.c b/external/badvpn_dns/flow/LineBuffer.c new file mode 100644 index 00000000..15c99699 --- /dev/null +++ b/external/badvpn_dns/flow/LineBuffer.c @@ -0,0 +1,140 @@ +/** + * @file LineBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#include + +static void input_handler_done (LineBuffer *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - o->buf_used) + + // update buffer + o->buf_used += data_len; + + // look for newline + int i; + for (i = o->buf_used - data_len; i < o->buf_used; i++) { + if (o->buf[i] == o->nl_char) { + break; + } + } + + if (i < o->buf_used || o->buf_used == o->buf_size) { + if (i == o->buf_used) { + BLog(BLOG_WARNING, "line too long"); + } + + // pass to output + o->buf_consumed = (i < o->buf_used ? i + 1 : i); + PacketPassInterface_Sender_Send(o->output, o->buf, o->buf_consumed); + } else { + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + } +} + +static void output_handler_done (LineBuffer *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buf_consumed > 0) + ASSERT(o->buf_consumed <= o->buf_used) + + // update buffer + memmove(o->buf, o->buf + o->buf_consumed, o->buf_used - o->buf_consumed); + o->buf_used -= o->buf_consumed; + + // look for newline + int i; + for (i = 0; i < o->buf_used; i++) { + if (o->buf[i] == o->nl_char) { + break; + } + } + + if (i < o->buf_used || o->buf_used == o->buf_size) { + // pass to output + o->buf_consumed = (i < o->buf_used ? i + 1 : i); + PacketPassInterface_Sender_Send(o->output, o->buf, o->buf_consumed); + } else { + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + } +} + +int LineBuffer_Init (LineBuffer *o, StreamRecvInterface *input, PacketPassInterface *output, int buf_size, uint8_t nl_char) +{ + ASSERT(buf_size > 0) + ASSERT(PacketPassInterface_GetMTU(output) >= buf_size) + + // init arguments + o->input = input; + o->output = output; + o->buf_size = buf_size; + o->nl_char = nl_char; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // set buffer empty + o->buf_used = 0; + + // allocate buffer + if (!(o->buf = (uint8_t *)malloc(o->buf_size))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void LineBuffer_Free (LineBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + free(o->buf); +} diff --git a/external/badvpn_dns/flow/LineBuffer.h b/external/badvpn_dns/flow/LineBuffer.h new file mode 100644 index 00000000..6e15e323 --- /dev/null +++ b/external/badvpn_dns/flow/LineBuffer.h @@ -0,0 +1,54 @@ +/** + * @file LineBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_FLOW_LINEBUFFER_H +#define BADVPN_FLOW_LINEBUFFER_H + +#include + +#include +#include +#include +#include + +typedef struct { + StreamRecvInterface *input; + PacketPassInterface *output; + int buf_size; + uint8_t nl_char; + int buf_used; + uint8_t *buf; + int buf_consumed; + DebugObject d_obj; +} LineBuffer; + +int LineBuffer_Init (LineBuffer *o, StreamRecvInterface *input, PacketPassInterface *output, int buf_size, uint8_t nl_char) WARN_UNUSED; +void LineBuffer_Free (LineBuffer *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketBuffer.c b/external/badvpn_dns/flow/PacketBuffer.c new file mode 100644 index 00000000..30afef61 --- /dev/null +++ b/external/badvpn_dns/flow/PacketBuffer.c @@ -0,0 +1,131 @@ +/** + * @file PacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (PacketBuffer *buf, int in_len); +static void output_handler_done (PacketBuffer *buf); + +void input_handler_done (PacketBuffer *buf, int in_len) +{ + ASSERT(in_len >= 0) + ASSERT(in_len <= buf->input_mtu) + DebugObject_Access(&buf->d_obj); + + // remember if buffer is empty + int was_empty = (buf->buf.output_avail < 0); + + // submit packet to buffer + ChunkBuffer2_SubmitPacket(&buf->buf, in_len); + + // if there is space, schedule receive + if (buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if buffer was empty, schedule send + if (was_empty) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +void output_handler_done (PacketBuffer *buf) +{ + DebugObject_Access(&buf->d_obj); + + // remember if buffer is full + int was_full = (buf->buf.input_avail < buf->input_mtu); + + // remove packet from buffer + ChunkBuffer2_ConsumePacket(&buf->buf); + + // if buffer was full and there is space, schedule receive + if (was_full && buf->buf.input_avail >= buf->input_mtu) { + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + } + + // if there is more data, schedule send + if (buf->buf.output_avail >= 0) { + PacketPassInterface_Sender_Send(buf->output, buf->buf.output_dest, buf->buf.output_avail); + } +} + +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + ASSERT(num_packets > 0) + + // init arguments + buf->input = input; + buf->output = output; + + // init input + PacketRecvInterface_Receiver_Init(buf->input, (PacketRecvInterface_handler_done)input_handler_done, buf); + + // set input MTU + buf->input_mtu = PacketRecvInterface_GetMTU(buf->input); + + // init output + PacketPassInterface_Sender_Init(buf->output, (PacketPassInterface_handler_done)output_handler_done, buf); + + // allocate buffer + int num_blocks = ChunkBuffer2_calc_blocks(buf->input_mtu, num_packets); + if (num_blocks < 0) { + goto fail0; + } + if (!(buf->buf_data = (struct ChunkBuffer2_block *)BAllocArray(num_blocks, sizeof(buf->buf_data[0])))) { + goto fail0; + } + + // init buffer + ChunkBuffer2_Init(&buf->buf, buf->buf_data, num_blocks, buf->input_mtu); + + // schedule receive + PacketRecvInterface_Receiver_Recv(buf->input, buf->buf.input_dest); + + DebugObject_Init(&buf->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketBuffer_Free (PacketBuffer *buf) +{ + DebugObject_Free(&buf->d_obj); + + // free buffer + BFree(buf->buf_data); +} diff --git a/external/badvpn_dns/flow/PacketBuffer.h b/external/badvpn_dns/flow/PacketBuffer.h new file mode 100644 index 00000000..6daab69b --- /dev/null +++ b/external/badvpn_dns/flow/PacketBuffer.h @@ -0,0 +1,77 @@ +/** + * @file PacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETBUFFER_H +#define BADVPN_FLOW_PACKETBUFFER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + int input_mtu; + PacketPassInterface *output; + struct ChunkBuffer2_block *buf_data; + ChunkBuffer2 buf; +} PacketBuffer; + +/** + * Initializes the buffer. + * Output MTU must be >= input MTU. + * + * @param buf the object + * @param input input interface + * @param output output interface + * @param num_packets minimum number of packets the buffer must hold. Must be >0. + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketBuffer_Init (PacketBuffer *buf, PacketRecvInterface *input, PacketPassInterface *output, int num_packets, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the buffer. + * + * @param buf the object + */ +void PacketBuffer_Free (PacketBuffer *buf); + +#endif diff --git a/external/badvpn_dns/flow/PacketCopier.c b/external/badvpn_dns/flow/PacketCopier.c new file mode 100644 index 00000000..f4c71264 --- /dev/null +++ b/external/badvpn_dns/flow/PacketCopier.c @@ -0,0 +1,136 @@ +/** + * @file PacketCopier.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (PacketCopier *o, uint8_t *data, int data_len) +{ + ASSERT(o->in_len == -1) + ASSERT(data_len >= 0) + DebugObject_Access(&o->d_obj); + + if (!o->out_have) { + o->in_len = data_len; + o->in = data; + return; + } + + memcpy(o->out, data, data_len); + + // finish input packet + PacketPassInterface_Done(&o->input); + + // finish output packet + PacketRecvInterface_Done(&o->output, data_len); + + o->out_have = 0; +} + +static void input_handler_requestcancel (PacketCopier *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // finish input packet + PacketPassInterface_Done(&o->input); + + o->in_len = -1; +} + +static void output_handler_recv (PacketCopier *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + if (o->in_len < 0) { + o->out_have = 1; + o->out = data; + return; + } + + memcpy(data, o->in, o->in_len); + + // finish input packet + PacketPassInterface_Done(&o->input); + + // finish output packet + PacketRecvInterface_Done(&o->output, o->in_len); + + o->in_len = -1; +} + +void PacketCopier_Init (PacketCopier *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init input + PacketPassInterface_Init(&o->input, mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + + // init output + PacketRecvInterface_Init(&o->output, mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // set no input packet + o->in_len = -1; + + // set no output packet + o->out_have = 0; + + DebugObject_Init(&o->d_obj); +} + +void PacketCopier_Free (PacketCopier *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketCopier_GetInput (PacketCopier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +PacketRecvInterface * PacketCopier_GetOutput (PacketCopier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/flow/PacketCopier.h b/external/badvpn_dns/flow/PacketCopier.h new file mode 100644 index 00000000..9b8ba863 --- /dev/null +++ b/external/badvpn_dns/flow/PacketCopier.h @@ -0,0 +1,90 @@ +/** + * @file PacketCopier.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which copies packets. + */ + +#ifndef BADVPN_FLOW_PACKETCOPIER_H +#define BADVPN_FLOW_PACKETCOPIER_H + +#include + +#include +#include + +/** + * Object which copies packets. + * Input is via {@link PacketPassInterface}. + * Output is via {@link PacketRecvInterface}. + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface input; + PacketRecvInterface output; + int in_len; + uint8_t *in; + int out_have; + uint8_t *out; +} PacketCopier; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. + * @param pg pending group + */ +void PacketCopier_Init (PacketCopier *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketCopier_Free (PacketCopier *o); + +/** + * Returns the input interface. + * The MTU of the interface will as in {@link PacketCopier_Init}. + * The interface will support cancel functionality. + * + * @return input interface + */ +PacketPassInterface * PacketCopier_GetInput (PacketCopier *o); + +/** + * Returns the output interface. + * The MTU of the interface will be as in {@link PacketCopier_Init}. + * + * @return output interface + */ +PacketRecvInterface * PacketCopier_GetOutput (PacketCopier *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassConnector.c b/external/badvpn_dns/flow/PacketPassConnector.c new file mode 100644 index 00000000..1a10326d --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassConnector.c @@ -0,0 +1,125 @@ +/** + * @file PacketPassConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (PacketPassConnector *o, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->input_mtu) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + o->in = data; + + if (o->output) { + // schedule send + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +static void output_handler_done (PacketPassConnector *o) +{ + ASSERT(o->in_len >= 0) + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // have no input packet + o->in_len = -1; + + // allow input to send more packets + PacketPassInterface_Done(&o->input); +} + +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + o->input_mtu = mtu; + + // init input + PacketPassInterface_Init(&o->input, o->input_mtu, (PacketPassInterface_handler_send)input_handler_send, o, pg); + + // have no input packet + o->in_len = -1; + + // have no output + o->output = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketPassConnector_Free (PacketPassConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output) +{ + ASSERT(!o->output) + ASSERT(PacketPassInterface_GetMTU(output) >= o->input_mtu) + DebugObject_Access(&o->d_obj); + + // set output + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // if we have an input packet, schedule send + if (o->in_len >= 0) { + PacketPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o) +{ + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // set no output + o->output = NULL; +} diff --git a/external/badvpn_dns/flow/PacketPassConnector.h b/external/badvpn_dns/flow/PacketPassConnector.h new file mode 100644 index 00000000..1a2cc6c7 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassConnector.h @@ -0,0 +1,102 @@ +/** + * @file PacketPassConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_PACKETPASSCONNECTOR_H +#define BADVPN_FLOW_PACKETPASSCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link PacketPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ +typedef struct { + PacketPassInterface input; + int input_mtu; + int in_len; + uint8_t *in; + PacketPassInterface *output; + DebugObject d_obj; +} PacketPassConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param mtu maximum input packet size. Must be >=0. + * @param pg pending group + */ +void PacketPassConnector_Init (PacketPassConnector *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassConnector_Free (PacketPassConnector *o); + +/** + * Returns the input interface. + * The MTU of the interface will be as in {@link PacketPassConnector_Init}. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassConnector_GetInput (PacketPassConnector *o); + +/** + * Connects output. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output output to connect. Its MTU must be >= MTU specified in + * {@link PacketPassConnector_Init}. + */ +void PacketPassConnector_ConnectOutput (PacketPassConnector *o, PacketPassInterface *output); + +/** + * Disconnects output. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void PacketPassConnector_DisconnectOutput (PacketPassConnector *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassFairQueue.c b/external/badvpn_dns/flow/PacketPassFairQueue.c new file mode 100644 index 00000000..bbfafe06 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassFairQueue.c @@ -0,0 +1,405 @@ +/** + * @file PacketPassFairQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include + +static int compare_flows (PacketPassFairQueueFlow *f1, PacketPassFairQueueFlow *f2) +{ + int cmp = B_COMPARE(f1->time, f2->time); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)f1, (uintptr_t)f2); +} + +#include "PacketPassFairQueue_tree.h" +#include + +static uint64_t get_current_time (PacketPassFairQueue *m) +{ + if (m->sending_flow) { + return m->sending_flow->time; + } + + uint64_t time = 0; // to remove warning + int have = 0; + + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (first_flow) { + ASSERT(first_flow->is_queued) + + time = first_flow->time; + have = 1; + } + + if (m->previous_flow) { + if (!have || m->previous_flow->time < time) { + time = m->previous_flow->time; + have = 1; + } + } + + return (have ? time : 0); +} + +static void increment_sent_flow (PacketPassFairQueueFlow *flow, uint64_t amount) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(amount <= FAIRQUEUE_MAX_TIME) + ASSERT(!flow->is_queued) + ASSERT(!m->sending_flow) + + // does time overflow? + if (amount > FAIRQUEUE_MAX_TIME - flow->time) { + // get time to subtract + uint64_t subtract; + PacketPassFairQueueFlow *first_flow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + if (!first_flow) { + subtract = flow->time; + } else { + ASSERT(first_flow->is_queued) + subtract = first_flow->time; + } + + // subtract time from all flows + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&m->flows_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + PacketPassFairQueueFlow *someflow = UPPER_OBJECT(list_node, PacketPassFairQueueFlow, list_node); + + // don't subtract more time than there is, except for the just finished flow, + // where we allow time to underflow and then overflow to the correct value after adding to it + if (subtract > someflow->time && someflow != flow) { + ASSERT(!someflow->is_queued) + someflow->time = 0; + } else { + someflow->time -= subtract; + } + } + } + + // add time to flow + flow->time += amount; +} + +static void schedule (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!m->freeing) + ASSERT(!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + + // get first queued flow + PacketPassFairQueueFlow *qflow = PacketPassFairQueue__Tree_GetFirst(&m->queued_tree, 0); + ASSERT(qflow->is_queued) + + // remove flow from queue + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, qflow); + qflow->is_queued = 0; + + // schedule send + PacketPassInterface_Sender_Send(m->output, qflow->queued.data, qflow->queued.data_len); + m->sending_flow = qflow; + m->sending_len = qflow->queued.data_len; +} + +static void schedule_job_handler (PacketPassFairQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // remove previous flow + m->previous_flow = NULL; + + if (!PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) { + schedule(m); + } +} + +static void input_handler_send (PacketPassFairQueueFlow *flow, uint8_t *data, int data_len) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow != m->sending_flow) + ASSERT(!flow->is_queued) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + if (flow == m->previous_flow) { + // remove from previous flow + m->previous_flow = NULL; + } else { + // raise time + flow->time = bmax_uint64(flow->time, get_current_time(m)); + } + + // queue flow + flow->queued.data = data; + flow->queued.data_len = data_len; + int res = PacketPassFairQueue__Tree_Insert(&m->queued_tree, 0, flow, NULL); + ASSERT_EXECUTE(res) + flow->is_queued = 1; + + if (!m->sending_flow && !BPending_IsSet(&m->schedule_job)) { + schedule(m); + } +} + +static void output_handler_done (PacketPassFairQueue *m) +{ + ASSERT(m->sending_flow) + ASSERT(!m->previous_flow) + ASSERT(!BPending_IsSet(&m->schedule_job)) + ASSERT(!m->freeing) + ASSERT(!m->sending_flow->is_queued) + + PacketPassFairQueueFlow *flow = m->sending_flow; + + // sending finished + m->sending_flow = NULL; + + // remember this flow so the schedule job can remove its time if it didn's send + m->previous_flow = flow; + + // update flow time by packet size + increment_sent_flow(flow, (uint64_t)m->packet_weight + m->sending_len); + + // schedule schedule + BPending_Set(&m->schedule_job); + + // finish flow packet + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassFairQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) +{ + ASSERT(packet_weight > 0) + ASSERT(use_cancel == 0 || use_cancel == 1) + ASSERT(!use_cancel || PacketPassInterface_HasCancel(output)) + + // init arguments + m->output = output; + m->pg = pg; + m->use_cancel = use_cancel; + m->packet_weight = packet_weight; + + // make sure that (output MTU + packet_weight <= FAIRQUEUE_MAX_TIME) + if (!( + (PacketPassInterface_GetMTU(output) <= FAIRQUEUE_MAX_TIME) && + (packet_weight <= FAIRQUEUE_MAX_TIME - PacketPassInterface_GetMTU(output)) + )) { + goto fail0; + } + + // init output + PacketPassInterface_Sender_Init(m->output, (PacketPassInterface_handler_done)output_handler_done, m); + + // not sending + m->sending_flow = NULL; + + // no previous flow + m->previous_flow = NULL; + + // init queued tree + PacketPassFairQueue__Tree_Init(&m->queued_tree); + + // init flows list + LinkedList1_Init(&m->flows_list); + + // not freeing + m->freeing = 0; + + // init schedule job + BPending_Init(&m->schedule_job, m->pg, (BPending_handler)schedule_job_handler, m); + + DebugObject_Init(&m->d_obj); + DebugCounter_Init(&m->d_ctr); + return 1; + +fail0: + return 0; +} + +void PacketPassFairQueue_Free (PacketPassFairQueue *m) +{ + ASSERT(LinkedList1_IsEmpty(&m->flows_list)) + ASSERT(PacketPassFairQueue__Tree_IsEmpty(&m->queued_tree)) + ASSERT(!m->previous_flow) + ASSERT(!m->sending_flow) + DebugCounter_Free(&m->d_ctr); + DebugObject_Free(&m->d_obj); + + // free schedule job + BPending_Free(&m->schedule_job); +} + +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + // set freeing + m->freeing = 1; +} + +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m) +{ + DebugObject_Access(&m->d_obj); + + return PacketPassInterface_GetMTU(m->output); +} + +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m) +{ + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // init arguments + flow->m = m; + + // have no canfree handler + flow->handler_busy = NULL; + + // init input + PacketPassInterface_Init(&flow->input, PacketPassInterface_GetMTU(flow->m->output), (PacketPassInterface_handler_send)input_handler_send, flow, m->pg); + + // set time + flow->time = 0; + + // add to flows list + LinkedList1_Append(&m->flows_list, &flow->list_node); + + // is not queued + flow->is_queued = 0; + + DebugObject_Init(&flow->d_obj); + DebugCounter_Increment(&m->d_ctr); +} + +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(m->freeing || flow != m->sending_flow) + DebugCounter_Decrement(&m->d_ctr); + DebugObject_Free(&flow->d_obj); + + // remove from current flow + if (flow == m->sending_flow) { + m->sending_flow = NULL; + } + + // remove from previous flow + if (flow == m->previous_flow) { + m->previous_flow = NULL; + } + + // remove from queue + if (flow->is_queued) { + PacketPassFairQueue__Tree_Remove(&m->queued_tree, 0, flow); + } + + // remove from flows list + LinkedList1_Remove(&m->flows_list, &flow->list_node); + + // free input + PacketPassInterface_Free(&flow->input); +} + +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(m->freeing || flow != m->sending_flow) + DebugObject_Access(&flow->d_obj); +} + +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + return (flow == m->sending_flow); +} + +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow) +{ + PacketPassFairQueue *m = flow->m; + + ASSERT(flow == m->sending_flow) + ASSERT(m->use_cancel) + ASSERT(!m->freeing) + ASSERT(!BPending_IsSet(&m->schedule_job)) + DebugObject_Access(&flow->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(m->output); +} + +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user) +{ + PacketPassFairQueue *m = flow->m; + B_USE(m) + + ASSERT(flow == m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // set handler + flow->handler_busy = handler; + flow->user = user; +} + +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow) +{ + DebugObject_Access(&flow->d_obj); + + return &flow->input; +} diff --git a/external/badvpn_dns/flow/PacketPassFairQueue.h b/external/badvpn_dns/flow/PacketPassFairQueue.h new file mode 100644 index 00000000..f8465964 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassFairQueue.h @@ -0,0 +1,204 @@ +/** + * @file PacketPassFairQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Fair queue using {@link PacketPassInterface}. + */ + +#ifndef BADVPN_FLOW_PACKETPASSFAIRQUEUE_H +#define BADVPN_FLOW_PACKETPASSFAIRQUEUE_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +// reduce this to test time overflow handling +#define FAIRQUEUE_MAX_TIME UINT64_MAX + +typedef void (*PacketPassFairQueue_handler_busy) (void *user); + +struct PacketPassFairQueueFlow_s; + +#include "PacketPassFairQueue_tree.h" +#include + +typedef struct PacketPassFairQueueFlow_s { + struct PacketPassFairQueue_s *m; + PacketPassFairQueue_handler_busy handler_busy; + void *user; + PacketPassInterface input; + uint64_t time; + LinkedList1Node list_node; + int is_queued; + struct { + PacketPassFairQueue__TreeNode tree_node; + uint8_t *data; + int data_len; + } queued; + DebugObject d_obj; +} PacketPassFairQueueFlow; + +/** + * Fair queue using {@link PacketPassInterface}. + */ +typedef struct PacketPassFairQueue_s { + PacketPassInterface *output; + BPendingGroup *pg; + int use_cancel; + int packet_weight; + struct PacketPassFairQueueFlow_s *sending_flow; + int sending_len; + struct PacketPassFairQueueFlow_s *previous_flow; + PacketPassFairQueue__Tree queued_tree; + LinkedList1 flows_list; + int freeing; + BPending schedule_job; + DebugObject d_obj; + DebugCounter d_ctr; +} PacketPassFairQueue; + +/** + * Initializes the queue. + * + * @param m the object + * @param output output interface + * @param pg pending group + * @param use_cancel whether cancel functionality is required. Must be 0 or 1. + * If 1, output must support cancel functionality. + * @param packet_weight additional weight a packet bears. Must be >0, to keep + * the queue fair for zero size packets. + * @return 1 on success, 0 on failure (because output MTU is too large) + */ +int PacketPassFairQueue_Init (PacketPassFairQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel, int packet_weight) WARN_UNUSED; + +/** + * Frees the queue. + * All flows must have been freed. + * + * @param m the object + */ +void PacketPassFairQueue_Free (PacketPassFairQueue *m); + +/** + * Prepares for freeing the entire queue. Must be called to allow freeing + * the flows in the process of freeing the entire queue. + * After this function is called, flows and the queue must be freed + * before any further I/O. + * May be called multiple times. + * The queue enters freeing state. + * + * @param m the object + */ +void PacketPassFairQueue_PrepareFree (PacketPassFairQueue *m); + +/** + * Returns the MTU of the queue. + * + * @param m the object + */ +int PacketPassFairQueue_GetMTU (PacketPassFairQueue *m); + +/** + * Initializes a queue flow. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param m queue to attach to + */ +void PacketPassFairQueueFlow_Init (PacketPassFairQueueFlow *flow, PacketPassFairQueue *m); + +/** + * Frees a queue flow. + * Unless the queue is in freeing state: + * - The flow must not be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * - Must not be called from queue calls to output. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_Free (PacketPassFairQueueFlow *flow); + +/** + * Does nothing. + * It must be possible to free the flow (see {@link PacketPassFairQueueFlow_Free}). + * + * @param flow the object + */ +void PacketPassFairQueueFlow_AssertFree (PacketPassFairQueueFlow *flow); + +/** + * Determines if the flow is busy. If the flow is considered busy, it must not + * be freed. At any given time, at most one flow will be indicated as busy. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @return 0 if not busy, 1 is busy + */ +int PacketPassFairQueueFlow_IsBusy (PacketPassFairQueueFlow *flow); + +/** + * Requests the output to stop processing the current packet as soon as possible. + * Cancel functionality must be enabled for the queue. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * + * @param flow the object + */ +void PacketPassFairQueueFlow_RequestCancel (PacketPassFairQueueFlow *flow); + +/** + * Sets up a callback to be called when the flow is no longer busy. + * The handler will be called as soon as the flow is no longer busy, i.e. it is not + * possible that this flow is no longer busy before the handler is called. + * The flow must be busy as indicated by {@link PacketPassFairQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param handler callback function. NULL to disable. + * @param user value passed to callback function. Ignored if handler is NULL. + */ +void PacketPassFairQueueFlow_SetBusyHandler (PacketPassFairQueueFlow *flow, PacketPassFairQueue_handler_busy handler, void *user); + +/** + * Returns the input interface of the flow. + * + * @param flow the object + * @return input interface + */ +PacketPassInterface * PacketPassFairQueueFlow_GetInput (PacketPassFairQueueFlow *flow); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassFairQueue_tree.h b/external/badvpn_dns/flow/PacketPassFairQueue_tree.h new file mode 100644 index 00000000..5dd0a7d4 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassFairQueue_tree.h @@ -0,0 +1,7 @@ +#define SAVL_PARAM_NAME PacketPassFairQueue__Tree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct PacketPassFairQueueFlow_s +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_flows((entry1), (entry2)) +#define SAVL_PARAM_MEMBER_NODE queued.tree_node diff --git a/external/badvpn_dns/flow/PacketPassFifoQueue.c b/external/badvpn_dns/flow/PacketPassFifoQueue.c new file mode 100644 index 00000000..03950065 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassFifoQueue.c @@ -0,0 +1,241 @@ +/** + * @file PacketPassFifoQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "PacketPassFifoQueue.h" + +static void schedule (PacketPassFifoQueue *o) +{ + ASSERT(!o->freeing) + ASSERT(!o->sending_flow) + ASSERT(!LinkedList1_IsEmpty(&o->waiting_flows_list)) + ASSERT(!BPending_IsSet(&o->schedule_job)) + + // get first waiting flow + PacketPassFifoQueueFlow *flow = UPPER_OBJECT(LinkedList1_GetFirst(&o->waiting_flows_list), PacketPassFifoQueueFlow, waiting_flows_list_node); + ASSERT(flow->queue == o) + ASSERT(flow->is_waiting) + + // remove it from queue + LinkedList1_Remove(&o->waiting_flows_list, &flow->waiting_flows_list_node); + flow->is_waiting = 0; + + // send + PacketPassInterface_Sender_Send(o->output, flow->waiting_data, flow->waiting_len); + o->sending_flow = flow; +} + +static void schedule_job_handler (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->freeing) + ASSERT(!o->sending_flow) + + if (!LinkedList1_IsEmpty(&o->waiting_flows_list)) { + schedule(o); + } +} + +static void input_handler_send (PacketPassFifoQueueFlow *o, uint8_t *data, int data_len) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Access(&o->d_obj); + ASSERT(!o->is_waiting) + ASSERT(o != queue->sending_flow) + ASSERT(!queue->freeing) + + // queue flow + o->waiting_data = data; + o->waiting_len = data_len; + LinkedList1_Append(&queue->waiting_flows_list, &o->waiting_flows_list_node); + o->is_waiting = 1; + + // schedule + if (!queue->sending_flow && !BPending_IsSet(&queue->schedule_job)) { + schedule(queue); + } +} + +static void output_handler_done (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sending_flow) + ASSERT(!BPending_IsSet(&o->schedule_job)) + ASSERT(!o->freeing) + ASSERT(!o->sending_flow->is_waiting) + + PacketPassFifoQueueFlow *flow = o->sending_flow; + + // set no sending flow + o->sending_flow = NULL; + + // schedule schedule job + BPending_Set(&o->schedule_job); + + // done input + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassFifoQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +void PacketPassFifoQueue_Init (PacketPassFifoQueue *o, PacketPassInterface *output, BPendingGroup *pg) +{ + // init arguments + o->output = output; + o->pg = pg; + + // init output + PacketPassInterface_Sender_Init(output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init waiting flows list + LinkedList1_Init(&o->waiting_flows_list); + + // set no sending flow + o->sending_flow = NULL; + + // init schedule job + BPending_Init(&o->schedule_job, pg, (BPending_handler)schedule_job_handler, o); + + // set not freeing + o->freeing = 0; + + DebugCounter_Init(&o->d_flows_ctr); + DebugObject_Init(&o->d_obj); +} + +void PacketPassFifoQueue_Free (PacketPassFifoQueue *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_flows_ctr); + ASSERT(LinkedList1_IsEmpty(&o->waiting_flows_list)) + ASSERT(!o->sending_flow) + + // free schedule job + BPending_Free(&o->schedule_job); +} + +void PacketPassFifoQueue_PrepareFree (PacketPassFifoQueue *o) +{ + DebugObject_Access(&o->d_obj); + + // set freeing + o->freeing = 1; +} + +void PacketPassFifoQueueFlow_Init (PacketPassFifoQueueFlow *o, PacketPassFifoQueue *queue) +{ + DebugObject_Access(&queue->d_obj); + ASSERT(!queue->freeing) + + // init arguments + o->queue = queue; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(queue->output), (PacketPassInterface_handler_send)input_handler_send, o, queue->pg); + + // set not waiting + o->is_waiting = 0; + + // set no busy handler + o->handler_busy = NULL; + + DebugCounter_Increment(&queue->d_flows_ctr); + DebugObject_Init(&o->d_obj); +} + +void PacketPassFifoQueueFlow_Free (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&queue->d_flows_ctr); + ASSERT(queue->freeing || o != queue->sending_flow) + + // remove from sending flow + if (o == queue->sending_flow) { + queue->sending_flow = NULL; + } + + // remove from waiting flows list + if (o->is_waiting) { + LinkedList1_Remove(&queue->waiting_flows_list, &o->waiting_flows_list_node); + } + + // free input + PacketPassInterface_Free(&o->input); +} + +void PacketPassFifoQueueFlow_AssertFree (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + B_USE(queue) + DebugObject_Access(&o->d_obj); + ASSERT(queue->freeing || o != queue->sending_flow) +} + +int PacketPassFifoQueueFlow_IsBusy (PacketPassFifoQueueFlow *o) +{ + PacketPassFifoQueue *queue = o->queue; + DebugObject_Access(&o->d_obj); + ASSERT(!queue->freeing) + + return (o == queue->sending_flow); +} + +void PacketPassFifoQueueFlow_SetBusyHandler (PacketPassFifoQueueFlow *o, PacketPassFifoQueue_handler_busy handler_busy, void *user) +{ + PacketPassFifoQueue *queue = o->queue; + B_USE(queue) + DebugObject_Access(&o->d_obj); + ASSERT(!queue->freeing) + ASSERT(o == queue->sending_flow) + + // set (or unset) busy handler + o->handler_busy = handler_busy; + o->user = user; +} + +PacketPassInterface * PacketPassFifoQueueFlow_GetInput (PacketPassFifoQueueFlow *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/external/badvpn_dns/flow/PacketPassFifoQueue.h b/external/badvpn_dns/flow/PacketPassFifoQueue.h new file mode 100644 index 00000000..cefe79b6 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassFifoQueue.h @@ -0,0 +1,76 @@ +/** + * @file PacketPassFifoQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PACKETPASSFIFOQUEUE_H +#define BADVPN_PACKETPASSFIFOQUEUE_H + +#include +#include +#include +#include + +typedef void (*PacketPassFifoQueue_handler_busy) (void *user); + +struct PacketPassFifoQueueFlow_s; + +typedef struct { + PacketPassInterface *output; + BPendingGroup *pg; + LinkedList1 waiting_flows_list; + struct PacketPassFifoQueueFlow_s *sending_flow; + BPending schedule_job; + int freeing; + DebugCounter d_flows_ctr; + DebugObject d_obj; +} PacketPassFifoQueue; + +typedef struct PacketPassFifoQueueFlow_s { + PacketPassFifoQueue *queue; + PacketPassInterface input; + int is_waiting; + LinkedList1Node waiting_flows_list_node; + uint8_t *waiting_data; + int waiting_len; + PacketPassFifoQueue_handler_busy handler_busy; + void *user; + DebugObject d_obj; +} PacketPassFifoQueueFlow; + +void PacketPassFifoQueue_Init (PacketPassFifoQueue *o, PacketPassInterface *output, BPendingGroup *pg); +void PacketPassFifoQueue_Free (PacketPassFifoQueue *o); +void PacketPassFifoQueue_PrepareFree (PacketPassFifoQueue *o); + +void PacketPassFifoQueueFlow_Init (PacketPassFifoQueueFlow *o, PacketPassFifoQueue *queue); +void PacketPassFifoQueueFlow_Free (PacketPassFifoQueueFlow *o); +void PacketPassFifoQueueFlow_AssertFree (PacketPassFifoQueueFlow *o); +int PacketPassFifoQueueFlow_IsBusy (PacketPassFifoQueueFlow *o); +void PacketPassFifoQueueFlow_SetBusyHandler (PacketPassFifoQueueFlow *o, PacketPassFifoQueue_handler_busy handler_busy, void *user); +PacketPassInterface * PacketPassFifoQueueFlow_GetInput (PacketPassFifoQueueFlow *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassInterface.c b/external/badvpn_dns/flow/PacketPassInterface.c new file mode 100644 index 00000000..dfa6ed55 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassInterface.c @@ -0,0 +1,68 @@ +/** + * @file PacketPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketPassInterface_job_operation (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + ASSERT(i->cancel_requested) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // call handler + i->handler_requestcancel(i->user_provider); + return; +} + +void _PacketPassInterface_job_done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user); + return; +} diff --git a/external/badvpn_dns/flow/PacketPassInterface.h b/external/badvpn_dns/flow/PacketPassInterface.h new file mode 100644 index 00000000..0cc22748 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassInterface.h @@ -0,0 +1,236 @@ +/** + * @file PacketPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet sender to pass data packets to a packet receiver. + */ + +#ifndef BADVPN_FLOW_PACKETPASSINTERFACE_H +#define BADVPN_FLOW_PACKETPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PPI_STATE_NONE 1 +#define PPI_STATE_OPERATION_PENDING 2 +#define PPI_STATE_BUSY 3 +#define PPI_STATE_DONE_PENDING 4 + +typedef void (*PacketPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*PacketPassInterface_handler_requestcancel) (void *user); + +typedef void (*PacketPassInterface_handler_done) (void *user); + +typedef struct { + // provider data + int mtu; + PacketPassInterface_handler_send handler_operation; + PacketPassInterface_handler_requestcancel handler_requestcancel; + void *user_provider; + + // user data + PacketPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // requestcancel job + BPending job_requestcancel; + + // done job + BPending job_done; + + // state + int state; + int cancel_requested; + + DebugObject d_obj; +} PacketPassInterface; + +static void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void PacketPassInterface_Free (PacketPassInterface *i); + +static void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel); + +static void PacketPassInterface_Done (PacketPassInterface *i); + +static int PacketPassInterface_GetMTU (PacketPassInterface *i); + +static void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user); + +static void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len); + +static void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i); + +static int PacketPassInterface_HasCancel (PacketPassInterface *i); + +void _PacketPassInterface_job_operation (PacketPassInterface *i); +void _PacketPassInterface_job_requestcancel (PacketPassInterface *i); +void _PacketPassInterface_job_done (PacketPassInterface *i); + +void PacketPassInterface_Init (PacketPassInterface *i, int mtu, PacketPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->handler_requestcancel = NULL; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketPassInterface_job_operation, i); + BPending_Init(&i->job_requestcancel, pg, (BPending_handler)_PacketPassInterface_job_requestcancel, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketPassInterface_job_done, i); + + // set state + i->state = PPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketPassInterface_Free (PacketPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_requestcancel); + BPending_Free(&i->job_operation); +} + +void PacketPassInterface_EnableCancel (PacketPassInterface *i, PacketPassInterface_handler_requestcancel handler_requestcancel) +{ + ASSERT(!i->handler_requestcancel) + ASSERT(!i->handler_done) + ASSERT(handler_requestcancel) + + i->handler_requestcancel = handler_requestcancel; +} + +void PacketPassInterface_Done (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // unset requestcancel job + BPending_Unset(&i->job_requestcancel); + + // schedule done + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; +} + +int PacketPassInterface_GetMTU (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketPassInterface_Sender_Init (PacketPassInterface *i, PacketPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketPassInterface_Sender_Send (PacketPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(!(data_len > 0) || data) + ASSERT(i->state == PPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = PPI_STATE_OPERATION_PENDING; + i->cancel_requested = 0; +} + +void PacketPassInterface_Sender_RequestCancel (PacketPassInterface *i) +{ + ASSERT(i->state == PPI_STATE_OPERATION_PENDING || i->state == PPI_STATE_BUSY || i->state == PPI_STATE_DONE_PENDING) + ASSERT(i->handler_requestcancel) + DebugObject_Access(&i->d_obj); + + // ignore multiple cancel requests + if (i->cancel_requested) { + return; + } + + // remember we requested cancel + i->cancel_requested = 1; + + if (i->state == PPI_STATE_OPERATION_PENDING) { + // unset operation job + BPending_Unset(&i->job_operation); + + // set done job + BPending_Set(&i->job_done); + + // set state + i->state = PPI_STATE_DONE_PENDING; + } else if (i->state == PPI_STATE_BUSY) { + // set requestcancel job + BPending_Set(&i->job_requestcancel); + } +} + +int PacketPassInterface_HasCancel (PacketPassInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return !!i->handler_requestcancel; +} + +#endif diff --git a/external/badvpn_dns/flow/PacketPassNotifier.c b/external/badvpn_dns/flow/PacketPassNotifier.c new file mode 100644 index 00000000..01327469 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassNotifier.c @@ -0,0 +1,103 @@ +/** + * @file PacketPassNotifier.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +void input_handler_send (PacketPassNotifier *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // schedule send + PacketPassInterface_Sender_Send(o->output, data, data_len); + + // if we have a handler, call it + if (o->handler) { + o->handler(o->handler_user, data, data_len); + return; + } +} + +void input_handler_requestcancel (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Sender_RequestCancel(o->output); +} + +void output_handler_done (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Done(&o->input); +} + +void PacketPassNotifier_Init (PacketPassNotifier *o, PacketPassInterface *output, BPendingGroup *pg) +{ + // init arguments + o->output = output; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, pg); + if (PacketPassInterface_HasCancel(o->output)) { + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + } + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // set no handler + o->handler = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketPassNotifier_Free (PacketPassNotifier *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassNotifier_GetInput (PacketPassNotifier *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassNotifier_SetHandler (PacketPassNotifier *o, PacketPassNotifier_handler_notify handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->handler_user = user; +} diff --git a/external/badvpn_dns/flow/PacketPassNotifier.h b/external/badvpn_dns/flow/PacketPassNotifier.h new file mode 100644 index 00000000..b2fab131 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassNotifier.h @@ -0,0 +1,99 @@ +/** + * @file PacketPassNotifier.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer which calles a handler function before + * passing a packet from input to output. + */ + +#ifndef BADVPN_FLOW_PACKETPASSNOTIFIER_H +#define BADVPN_FLOW_PACKETPASSNOTIFIER_H + +#include + +#include +#include + +/** + * Handler function called when input calls Send, but before the call is passed on to output. + * + * @param user value specified in {@link PacketPassNotifier_SetHandler} + * @param data packet provided by input + * @param data_len size of the packet + */ +typedef void (*PacketPassNotifier_handler_notify) (void *user, uint8_t *data, int data_len); + +/** + * A {@link PacketPassInterface} layer which calles a handler function before + * passing a packet from input to output. + */ +typedef struct { + PacketPassInterface input; + PacketPassInterface *output; + PacketPassNotifier_handler_notify handler; + void *handler_user; + DebugObject d_obj; +} PacketPassNotifier; + +/** + * Initializes the object. + * + * @param o the object + * @param output output interface + * @param pg pending group + */ +void PacketPassNotifier_Init (PacketPassNotifier *o, PacketPassInterface *output, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassNotifier_Free (PacketPassNotifier *o); + +/** + * Returns the input interface. + * The MTU of the interface will be the same as of the output interface. + * The interface supports cancel functionality if the output interface supports it. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassNotifier_GetInput (PacketPassNotifier *o); + +/** + * Configures a handler function to call before passing input packets to output. + * + * @param o the object + * @param handler handler function, or NULL to disable. + * @param user value to pass to handler function. Ignored if handler is NULL. + */ +void PacketPassNotifier_SetHandler (PacketPassNotifier *o, PacketPassNotifier_handler_notify handler, void *user); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassPriorityQueue.c b/external/badvpn_dns/flow/PacketPassPriorityQueue.c new file mode 100644 index 00000000..9534f1b1 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassPriorityQueue.c @@ -0,0 +1,283 @@ +/** + * @file PacketPassPriorityQueue.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +static int compare_flows (PacketPassPriorityQueueFlow *f1, PacketPassPriorityQueueFlow *f2) +{ + int cmp = B_COMPARE(f1->priority, f2->priority); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)f1, (uintptr_t)f2); +} + +#include "PacketPassPriorityQueue_tree.h" +#include + +static void schedule (PacketPassPriorityQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + ASSERT(!PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) + + // get first queued flow + PacketPassPriorityQueueFlow *qflow = PacketPassPriorityQueue__Tree_GetFirst(&m->queued_tree, 0); + ASSERT(qflow->is_queued) + + // remove flow from queue + PacketPassPriorityQueue__Tree_Remove(&m->queued_tree, 0, qflow); + qflow->is_queued = 0; + + // schedule send + PacketPassInterface_Sender_Send(m->output, qflow->queued.data, qflow->queued.data_len); + m->sending_flow = qflow; +} + +static void schedule_job_handler (PacketPassPriorityQueue *m) +{ + ASSERT(!m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + if (!PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) { + schedule(m); + } +} + +static void input_handler_send (PacketPassPriorityQueueFlow *flow, uint8_t *data, int data_len) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(flow != m->sending_flow) + ASSERT(!flow->is_queued) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // queue flow + flow->queued.data = data; + flow->queued.data_len = data_len; + int res = PacketPassPriorityQueue__Tree_Insert(&m->queued_tree, 0, flow, NULL); + ASSERT_EXECUTE(res) + flow->is_queued = 1; + + if (!m->sending_flow && !BPending_IsSet(&m->schedule_job)) { + schedule(m); + } +} + +static void output_handler_done (PacketPassPriorityQueue *m) +{ + ASSERT(m->sending_flow) + ASSERT(!BPending_IsSet(&m->schedule_job)) + ASSERT(!m->freeing) + ASSERT(!m->sending_flow->is_queued) + + PacketPassPriorityQueueFlow *flow = m->sending_flow; + + // sending finished + m->sending_flow = NULL; + + // schedule schedule + BPending_Set(&m->schedule_job); + + // finish flow packet + PacketPassInterface_Done(&flow->input); + + // call busy handler if set + if (flow->handler_busy) { + // handler is one-shot, unset it before calling + PacketPassPriorityQueue_handler_busy handler = flow->handler_busy; + flow->handler_busy = NULL; + + // call handler + handler(flow->user); + return; + } +} + +void PacketPassPriorityQueue_Init (PacketPassPriorityQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel) +{ + ASSERT(use_cancel == 0 || use_cancel == 1) + ASSERT(!use_cancel || PacketPassInterface_HasCancel(output)) + + // init arguments + m->output = output; + m->pg = pg; + m->use_cancel = use_cancel; + + // init output + PacketPassInterface_Sender_Init(m->output, (PacketPassInterface_handler_done)output_handler_done, m); + + // not sending + m->sending_flow = NULL; + + // init queued tree + PacketPassPriorityQueue__Tree_Init(&m->queued_tree); + + // not freeing + m->freeing = 0; + + // init schedule job + BPending_Init(&m->schedule_job, m->pg, (BPending_handler)schedule_job_handler, m); + + DebugObject_Init(&m->d_obj); + DebugCounter_Init(&m->d_ctr); +} + +void PacketPassPriorityQueue_Free (PacketPassPriorityQueue *m) +{ + ASSERT(PacketPassPriorityQueue__Tree_IsEmpty(&m->queued_tree)) + ASSERT(!m->sending_flow) + DebugCounter_Free(&m->d_ctr); + DebugObject_Free(&m->d_obj); + + // free schedule job + BPending_Free(&m->schedule_job); +} + +void PacketPassPriorityQueue_PrepareFree (PacketPassPriorityQueue *m) +{ + DebugObject_Access(&m->d_obj); + + // set freeing + m->freeing = 1; +} + +int PacketPassPriorityQueue_GetMTU (PacketPassPriorityQueue *m) +{ + DebugObject_Access(&m->d_obj); + + return PacketPassInterface_GetMTU(m->output); +} + +void PacketPassPriorityQueueFlow_Init (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue *m, int priority) +{ + ASSERT(!m->freeing) + DebugObject_Access(&m->d_obj); + + // init arguments + flow->m = m; + flow->priority = priority; + + // have no canfree handler + flow->handler_busy = NULL; + + // init input + PacketPassInterface_Init(&flow->input, PacketPassInterface_GetMTU(flow->m->output), (PacketPassInterface_handler_send)input_handler_send, flow, m->pg); + + // is not queued + flow->is_queued = 0; + + DebugObject_Init(&flow->d_obj); + DebugCounter_Increment(&m->d_ctr); +} + +void PacketPassPriorityQueueFlow_Free (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(m->freeing || flow != m->sending_flow) + DebugCounter_Decrement(&m->d_ctr); + DebugObject_Free(&flow->d_obj); + + // remove from current flow + if (flow == m->sending_flow) { + m->sending_flow = NULL; + } + + // remove from queue + if (flow->is_queued) { + PacketPassPriorityQueue__Tree_Remove(&m->queued_tree, 0, flow); + } + + // free input + PacketPassInterface_Free(&flow->input); +} + +void PacketPassPriorityQueueFlow_AssertFree (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + B_USE(m) + + ASSERT(m->freeing || flow != m->sending_flow) + DebugObject_Access(&flow->d_obj); +} + +int PacketPassPriorityQueueFlow_IsBusy (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + return (flow == m->sending_flow); +} + +void PacketPassPriorityQueueFlow_RequestCancel (PacketPassPriorityQueueFlow *flow) +{ + PacketPassPriorityQueue *m = flow->m; + + ASSERT(flow == m->sending_flow) + ASSERT(m->use_cancel) + ASSERT(!m->freeing) + ASSERT(!BPending_IsSet(&m->schedule_job)) + DebugObject_Access(&flow->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(m->output); +} + +void PacketPassPriorityQueueFlow_SetBusyHandler (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue_handler_busy handler, void *user) +{ + PacketPassPriorityQueue *m = flow->m; + B_USE(m) + + ASSERT(flow == m->sending_flow) + ASSERT(!m->freeing) + DebugObject_Access(&flow->d_obj); + + // set handler + flow->handler_busy = handler; + flow->user = user; +} + +PacketPassInterface * PacketPassPriorityQueueFlow_GetInput (PacketPassPriorityQueueFlow *flow) +{ + DebugObject_Access(&flow->d_obj); + + return &flow->input; +} diff --git a/external/badvpn_dns/flow/PacketPassPriorityQueue.h b/external/badvpn_dns/flow/PacketPassPriorityQueue.h new file mode 100644 index 00000000..3ac78eb5 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassPriorityQueue.h @@ -0,0 +1,192 @@ +/** + * @file PacketPassPriorityQueue.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Priority queue using {@link PacketPassInterface}. + */ + +#ifndef BADVPN_FLOW_PACKETPASSPRIORITYQUEUE_H +#define BADVPN_FLOW_PACKETPASSPRIORITYQUEUE_H + +#include + +#include +#include +#include +#include +#include + +typedef void (*PacketPassPriorityQueue_handler_busy) (void *user); + +struct PacketPassPriorityQueueFlow_s; + +#include "PacketPassPriorityQueue_tree.h" +#include + +typedef struct PacketPassPriorityQueueFlow_s { + struct PacketPassPriorityQueue_s *m; + int priority; + PacketPassPriorityQueue_handler_busy handler_busy; + void *user; + PacketPassInterface input; + int is_queued; + struct { + PacketPassPriorityQueue__TreeNode tree_node; + uint8_t *data; + int data_len; + } queued; + DebugObject d_obj; +} PacketPassPriorityQueueFlow; + +/** + * Priority queue using {@link PacketPassInterface}. + */ +typedef struct PacketPassPriorityQueue_s { + PacketPassInterface *output; + BPendingGroup *pg; + int use_cancel; + struct PacketPassPriorityQueueFlow_s *sending_flow; + PacketPassPriorityQueue__Tree queued_tree; + int freeing; + BPending schedule_job; + DebugObject d_obj; + DebugCounter d_ctr; +} PacketPassPriorityQueue; + +/** + * Initializes the queue. + * + * @param m the object + * @param output output interface + * @param pg pending group + * @param use_cancel whether cancel functionality is required. Must be 0 or 1. + * If 1, output must support cancel functionality. + */ +void PacketPassPriorityQueue_Init (PacketPassPriorityQueue *m, PacketPassInterface *output, BPendingGroup *pg, int use_cancel); + +/** + * Frees the queue. + * All flows must have been freed. + * + * @param m the object + */ +void PacketPassPriorityQueue_Free (PacketPassPriorityQueue *m); + +/** + * Prepares for freeing the entire queue. Must be called to allow freeing + * the flows in the process of freeing the entire queue. + * After this function is called, flows and the queue must be freed + * before any further I/O. + * May be called multiple times. + * The queue enters freeing state. + * + * @param m the object + */ +void PacketPassPriorityQueue_PrepareFree (PacketPassPriorityQueue *m); + +/** + * Returns the MTU of the queue. + * + * @param m the object + */ +int PacketPassPriorityQueue_GetMTU (PacketPassPriorityQueue *m); + +/** + * Initializes a queue flow. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param m queue to attach to + * @param priority flow priority. Lower value means higher priority. + */ +void PacketPassPriorityQueueFlow_Init (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue *m, int priority); + +/** + * Frees a queue flow. + * Unless the queue is in freeing state: + * - The flow must not be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * - Must not be called from queue calls to output. + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_Free (PacketPassPriorityQueueFlow *flow); + +/** + * Does nothing. + * It must be possible to free the flow (see {@link PacketPassPriorityQueueFlow}). + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_AssertFree (PacketPassPriorityQueueFlow *flow); + +/** + * Determines if the flow is busy. If the flow is considered busy, it must not + * be freed. At any given time, at most one flow will be indicated as busy. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @return 0 if not busy, 1 is busy + */ +int PacketPassPriorityQueueFlow_IsBusy (PacketPassPriorityQueueFlow *flow); + +/** + * Requests the output to stop processing the current packet as soon as possible. + * Cancel functionality must be enabled for the queue. + * The flow must be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * + * @param flow the object + */ +void PacketPassPriorityQueueFlow_RequestCancel (PacketPassPriorityQueueFlow *flow); + +/** + * Sets up a callback to be called when the flow is no longer busy. + * The handler will be called as soon as the flow is no longer busy, i.e. it is not + * possible that this flow is no longer busy before the handler is called. + * The flow must be busy as indicated by {@link PacketPassPriorityQueueFlow_IsBusy}. + * Queue must not be in freeing state. + * Must not be called from queue calls to output. + * + * @param flow the object + * @param handler callback function. NULL to disable. + * @param user value passed to callback function. Ignored if handler is NULL. + */ +void PacketPassPriorityQueueFlow_SetBusyHandler (PacketPassPriorityQueueFlow *flow, PacketPassPriorityQueue_handler_busy handler, void *user); + +/** + * Returns the input interface of the flow. + * + * @param flow the object + * @return input interface + */ +PacketPassInterface * PacketPassPriorityQueueFlow_GetInput (PacketPassPriorityQueueFlow *flow); + +#endif diff --git a/external/badvpn_dns/flow/PacketPassPriorityQueue_tree.h b/external/badvpn_dns/flow/PacketPassPriorityQueue_tree.h new file mode 100644 index 00000000..0d8b2133 --- /dev/null +++ b/external/badvpn_dns/flow/PacketPassPriorityQueue_tree.h @@ -0,0 +1,7 @@ +#define SAVL_PARAM_NAME PacketPassPriorityQueue__Tree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 1 +#define SAVL_PARAM_TYPE_ENTRY struct PacketPassPriorityQueueFlow_s +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_flows((entry1), (entry2)) +#define SAVL_PARAM_MEMBER_NODE queued.tree_node diff --git a/external/badvpn_dns/flow/PacketProtoDecoder.c b/external/badvpn_dns/flow/PacketProtoDecoder.c new file mode 100644 index 00000000..68d26c5b --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoDecoder.c @@ -0,0 +1,182 @@ +/** + * @file PacketProtoDecoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static void process_data (PacketProtoDecoder *enc); +static void input_handler_done (PacketProtoDecoder *enc, int data_len); +static void output_handler_done (PacketProtoDecoder *enc); + +void process_data (PacketProtoDecoder *enc) +{ + int was_error = 0; + + do { + uint8_t *data = enc->buf + enc->buf_start; + int left = enc->buf_used; + + // check if header was received + if (left < sizeof(struct packetproto_header)) { + break; + } + struct packetproto_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(struct packetproto_header); + left -= sizeof(struct packetproto_header); + int data_len = ltoh16(header.len); + + // check data length + if (data_len > enc->output_mtu) { + BLog(BLOG_NOTICE, "error: packet too large"); + was_error = 1; + break; + } + + // check if whole packet was received + if (left < data_len) { + break; + } + + // update buffer + enc->buf_start += sizeof(struct packetproto_header) + data_len; + enc->buf_used -= sizeof(struct packetproto_header) + data_len; + + // submit packet + PacketPassInterface_Sender_Send(enc->output, data, data_len); + return; + } while (0); + + if (was_error) { + // reset buffer + enc->buf_start = 0; + enc->buf_used = 0; + } else { + // if we reached the end of the buffer, wrap around to allow more data to be received + if (enc->buf_start + enc->buf_used == enc->buf_size) { + memmove(enc->buf, enc->buf + enc->buf_start, enc->buf_used); + enc->buf_start = 0; + } + } + + // receive data + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf + (enc->buf_start + enc->buf_used), enc->buf_size - (enc->buf_start + enc->buf_used)); + + // if we had error, report it + if (was_error) { + enc->handler_error(enc->user); + return; + } +} + +static void input_handler_done (PacketProtoDecoder *enc, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= enc->buf_size - (enc->buf_start + enc->buf_used)) + DebugObject_Access(&enc->d_obj); + + // update buffer + enc->buf_used += data_len; + + // process data + process_data(enc); + return; +} + +void output_handler_done (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + // process data + process_data(enc); + return; +} + +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) +{ + // init arguments + enc->input = input; + enc->output = output; + enc->user = user; + enc->handler_error = handler_error; + + // init input + StreamRecvInterface_Receiver_Init(enc->input, (StreamRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketPassInterface_Sender_Init(enc->output, (PacketPassInterface_handler_done)output_handler_done, enc); + + // set output MTU, limit by maximum payload size + enc->output_mtu = bmin_int(PacketPassInterface_GetMTU(enc->output), PACKETPROTO_MAXPAYLOAD); + + // init buffer state + enc->buf_size = PACKETPROTO_ENCLEN(enc->output_mtu); + enc->buf_start = 0; + enc->buf_used = 0; + + // allocate buffer + if (!(enc->buf = (uint8_t *)malloc(enc->buf_size))) { + goto fail0; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(enc->input, enc->buf, enc->buf_size); + + DebugObject_Init(&enc->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketProtoDecoder_Free (PacketProtoDecoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free buffer + free(enc->buf); +} + +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + enc->buf_start += enc->buf_used; + enc->buf_used = 0; +} diff --git a/external/badvpn_dns/flow/PacketProtoDecoder.h b/external/badvpn_dns/flow/PacketProtoDecoder.h new file mode 100644 index 00000000..2c20694b --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoDecoder.h @@ -0,0 +1,96 @@ +/** + * @file PacketProtoDecoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which decodes a stream according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTODECODER_H +#define BADVPN_FLOW_PACKETPROTODECODER_H + +#include + +#include +#include +#include +#include +#include + +/** + * Handler called when a protocol error occurs. + * When an error occurs, the decoder is reset to the initial state. + * + * @param user as in {@link PacketProtoDecoder_Init} + */ +typedef void (*PacketProtoDecoder_handler_error) (void *user); + +typedef struct { + StreamRecvInterface *input; + PacketPassInterface *output; + void *user; + PacketProtoDecoder_handler_error handler_error; + int output_mtu; + int buf_size; + int buf_start; + int buf_used; + uint8_t *buf; + DebugObject d_obj; +} PacketProtoDecoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. The decoder will accept packets with payload size up to its MTU + * (but the payload can never be more than PACKETPROTO_MAXPAYLOAD). + * @param output output interface + * @param pg pending group + * @param user argument to handlers + * @param handler_error error handler + * @return 1 on success, 0 on failure + */ +int PacketProtoDecoder_Init (PacketProtoDecoder *enc, StreamRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg, void *user, PacketProtoDecoder_handler_error handler_error) WARN_UNUSED; + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoDecoder_Free (PacketProtoDecoder *enc); + +/** + * Clears the internal buffer. + * The next data received from the input will be treated as a new + * PacketProto stream. + * + * @param enc the object + */ +void PacketProtoDecoder_Reset (PacketProtoDecoder *enc); + +#endif diff --git a/external/badvpn_dns/flow/PacketProtoEncoder.c b/external/badvpn_dns/flow/PacketProtoEncoder.c new file mode 100644 index 00000000..00aaa953 --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoEncoder.c @@ -0,0 +1,101 @@ +/** + * @file PacketProtoEncoder.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +static void output_handler_recv (PacketProtoEncoder *enc, uint8_t *data) +{ + ASSERT(!enc->output_packet) + ASSERT(data) + DebugObject_Access(&enc->d_obj); + + // schedule receive + enc->output_packet = data; + PacketRecvInterface_Receiver_Recv(enc->input, enc->output_packet + sizeof(struct packetproto_header)); +} + +static void input_handler_done (PacketProtoEncoder *enc, int in_len) +{ + ASSERT(enc->output_packet) + DebugObject_Access(&enc->d_obj); + + // write length + struct packetproto_header pp; + pp.len = htol16(in_len); + memcpy(enc->output_packet, &pp, sizeof(pp)); + + // finish output packet + enc->output_packet = NULL; + PacketRecvInterface_Done(&enc->output, PACKETPROTO_ENCLEN(in_len)); +} + +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg) +{ + ASSERT(PacketRecvInterface_GetMTU(input) <= PACKETPROTO_MAXPAYLOAD) + + // init arguments + enc->input = input; + + // init input + PacketRecvInterface_Receiver_Init(enc->input, (PacketRecvInterface_handler_done)input_handler_done, enc); + + // init output + PacketRecvInterface_Init( + &enc->output, PACKETPROTO_ENCLEN(PacketRecvInterface_GetMTU(enc->input)), + (PacketRecvInterface_handler_recv)output_handler_recv, enc, pg + ); + + // set no output packet + enc->output_packet = NULL; + + DebugObject_Init(&enc->d_obj); +} + +void PacketProtoEncoder_Free (PacketProtoEncoder *enc) +{ + DebugObject_Free(&enc->d_obj); + + // free input + PacketRecvInterface_Free(&enc->output); +} + +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc) +{ + DebugObject_Access(&enc->d_obj); + + return &enc->output; +} diff --git a/external/badvpn_dns/flow/PacketProtoEncoder.h b/external/badvpn_dns/flow/PacketProtoEncoder.h new file mode 100644 index 00000000..022aa008 --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoEncoder.h @@ -0,0 +1,80 @@ +/** + * @file PacketProtoEncoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which encodes packets according to PacketProto. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOENCODER_H +#define BADVPN_FLOW_PACKETPROTOENCODER_H + +#include + +#include +#include + +/** + * Object which encodes packets according to PacketProto. + * + * Input is with {@link PacketRecvInterface}. + * Output is with {@link PacketRecvInterface}. + */ +typedef struct { + PacketRecvInterface *input; + PacketRecvInterface output; + uint8_t *output_packet; + DebugObject d_obj; +} PacketProtoEncoder; + +/** + * Initializes the object. + * + * @param enc the object + * @param input input interface. Its MTU must be <=PACKETPROTO_MAXPAYLOAD. + * @param pg pending group + */ +void PacketProtoEncoder_Init (PacketProtoEncoder *enc, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param enc the object + */ +void PacketProtoEncoder_Free (PacketProtoEncoder *enc); + +/** + * Returns the output interface. + * The MTU of the output interface is PACKETPROTO_ENCLEN(MTU of input interface). + * + * @param enc the object + * @return output interface + */ +PacketRecvInterface * PacketProtoEncoder_GetOutput (PacketProtoEncoder *enc); + +#endif diff --git a/external/badvpn_dns/flow/PacketProtoFlow.c b/external/badvpn_dns/flow/PacketProtoFlow.c new file mode 100644 index 00000000..8fad8e20 --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoFlow.c @@ -0,0 +1,82 @@ +/** + * @file PacketProtoFlow.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(input_mtu >= 0) + ASSERT(input_mtu <= PACKETPROTO_MAXPAYLOAD) + ASSERT(num_packets > 0) + ASSERT(PacketPassInterface_GetMTU(output) >= PACKETPROTO_ENCLEN(input_mtu)) + + // init async input + BufferWriter_Init(&o->ainput, input_mtu, pg); + + // init encoder + PacketProtoEncoder_Init(&o->encoder, BufferWriter_GetOutput(&o->ainput), pg); + + // init buffer + if (!PacketBuffer_Init(&o->buffer, PacketProtoEncoder_GetOutput(&o->encoder), output, num_packets, pg)) { + goto fail0; + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + PacketProtoEncoder_Free(&o->encoder); + BufferWriter_Free(&o->ainput); + return 0; +} + +void PacketProtoFlow_Free (PacketProtoFlow *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + PacketBuffer_Free(&o->buffer); + + // free encoder + PacketProtoEncoder_Free(&o->encoder); + + // free async input + BufferWriter_Free(&o->ainput); +} + +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->ainput; +} diff --git a/external/badvpn_dns/flow/PacketProtoFlow.h b/external/badvpn_dns/flow/PacketProtoFlow.h new file mode 100644 index 00000000..05e12338 --- /dev/null +++ b/external/badvpn_dns/flow/PacketProtoFlow.h @@ -0,0 +1,83 @@ +/** + * @file PacketProtoFlow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ + +#ifndef BADVPN_FLOW_PACKETPROTOFLOW_H +#define BADVPN_FLOW_PACKETPROTOFLOW_H + +#include + +#include +#include +#include +#include + +/** + * Buffer which encodes packets with PacketProto, with {@link BufferWriter} + * input and {@link PacketPassInterface} output. + */ +typedef struct { + BufferWriter ainput; + PacketProtoEncoder encoder; + PacketBuffer buffer; + DebugObject d_obj; +} PacketProtoFlow; + +/** + * Initializes the object. + * + * @param o the object + * @param input_mtu maximum input packet size. Must be >=0 and <=PACKETPROTO_MAXPAYLOAD. + * @param num_packets minimum number of packets the buffer should hold. Must be >0. + * @param output output interface. Its MTU must be >=PACKETPROTO_ENCLEN(input_mtu). + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketProtoFlow_Init (PacketProtoFlow *o, int input_mtu, int num_packets, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void PacketProtoFlow_Free (PacketProtoFlow *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +BufferWriter * PacketProtoFlow_GetInput (PacketProtoFlow *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketRecvBlocker.c b/external/badvpn_dns/flow/PacketRecvBlocker.c new file mode 100644 index 00000000..7e679f88 --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvBlocker.c @@ -0,0 +1,99 @@ +/** + * @file PacketRecvBlocker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void output_handler_recv (PacketRecvBlocker *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // remember packet + o->out_have = 1; + o->out = data; + o->out_input_blocking = 0; +} + +static void input_handler_done (PacketRecvBlocker *o, int data_len) +{ + ASSERT(o->out_have) + ASSERT(o->out_input_blocking) + DebugObject_Access(&o->d_obj); + + // schedule done + o->out_have = 0; + PacketRecvInterface_Done(&o->output, data_len); +} + +void PacketRecvBlocker_Init (PacketRecvBlocker *o, PacketRecvInterface *input, BPendingGroup *pg) +{ + // init arguments + o->input = input; + + // init output + PacketRecvInterface_Init(&o->output, PacketRecvInterface_GetMTU(o->input), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_have = 0; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + DebugObject_Init(&o->d_obj); +} + +void PacketRecvBlocker_Free (PacketRecvBlocker *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * PacketRecvBlocker_GetOutput (PacketRecvBlocker *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void PacketRecvBlocker_AllowBlockedPacket (PacketRecvBlocker *o) +{ + DebugObject_Access(&o->d_obj); + + if (!o->out_have || o->out_input_blocking) { + return; + } + + // schedule receive + o->out_input_blocking = 1; + PacketRecvInterface_Receiver_Recv(o->input, o->out); +} diff --git a/external/badvpn_dns/flow/PacketRecvBlocker.h b/external/badvpn_dns/flow/PacketRecvBlocker.h new file mode 100644 index 00000000..ba98d064 --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvBlocker.h @@ -0,0 +1,90 @@ +/** + * @file PacketRecvBlocker.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link PacketRecvInterface} layer which blocks all output recv calls and only + * passes a single blocked call on to input when the user wants so. + */ + +#ifndef BADVPN_FLOW_PACKETRECVBLOCKER_H +#define BADVPN_FLOW_PACKETRECVBLOCKER_H + +#include + +#include +#include + +/** + * {@link PacketRecvInterface} layer which blocks all output recv calls and only + * passes a single blocked call on to input when the user wants so. + */ +typedef struct { + PacketRecvInterface output; + int out_have; + uint8_t *out; + int out_input_blocking; + PacketRecvInterface *input; + DebugObject d_obj; +} PacketRecvBlocker; + +/** + * Initializes the object. + * + * @param o the object + * @param input input interface + */ +void PacketRecvBlocker_Init (PacketRecvBlocker *o, PacketRecvInterface *input, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRecvBlocker_Free (PacketRecvBlocker *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be the same as of the input interface. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * PacketRecvBlocker_GetOutput (PacketRecvBlocker *o); + +/** + * Passes a blocked output recv call to input if there is one and it has not + * been passed yet. Otherwise it does nothing. + * Must not be called from input Recv calls. + * This function may invoke I/O. + * + * @param o the object + */ +void PacketRecvBlocker_AllowBlockedPacket (PacketRecvBlocker *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketRecvConnector.c b/external/badvpn_dns/flow/PacketRecvConnector.c new file mode 100644 index 00000000..455db230 --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvConnector.c @@ -0,0 +1,123 @@ +/** + * @file PacketRecvConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void output_handler_recv (PacketRecvConnector *o, uint8_t *data) +{ + ASSERT(!o->out_have) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_have = 1; + o->out = data; + + if (o->input) { + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, o->out); + } +} + +static void input_handler_done (PacketRecvConnector *o, int data_len) +{ + ASSERT(o->out_have) + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // have no output packet + o->out_have = 0; + + // allow output to receive more packets + PacketRecvInterface_Done(&o->output, data_len); +} + +void PacketRecvConnector_Init (PacketRecvConnector *o, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + o->output_mtu = mtu; + + // init output + PacketRecvInterface_Init(&o->output, o->output_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_have = 0; + + // have no input + o->input = NULL; + + DebugObject_Init(&o->d_obj); +} + +void PacketRecvConnector_Free (PacketRecvConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * PacketRecvConnector_GetOutput (PacketRecvConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void PacketRecvConnector_ConnectInput (PacketRecvConnector *o, PacketRecvInterface *input) +{ + ASSERT(!o->input) + ASSERT(PacketRecvInterface_GetMTU(input) <= o->output_mtu) + DebugObject_Access(&o->d_obj); + + // set input + o->input = input; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // if we have an output packet, schedule receive + if (o->out_have) { + PacketRecvInterface_Receiver_Recv(o->input, o->out); + } +} + +void PacketRecvConnector_DisconnectInput (PacketRecvConnector *o) +{ + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // set no input + o->input = NULL; +} diff --git a/external/badvpn_dns/flow/PacketRecvConnector.h b/external/badvpn_dns/flow/PacketRecvConnector.h new file mode 100644 index 00000000..25cf851d --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvConnector.h @@ -0,0 +1,102 @@ +/** + * @file PacketRecvConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_PACKETRECVCONNECTOR_H +#define BADVPN_FLOW_PACKETRECVCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link PacketRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ +typedef struct { + PacketRecvInterface output; + int output_mtu; + int out_have; + uint8_t *out; + PacketRecvInterface *input; + DebugObject d_obj; +} PacketRecvConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param mtu maximum output packet size. Must be >=0. + * @param pg pending group + */ +void PacketRecvConnector_Init (PacketRecvConnector *o, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRecvConnector_Free (PacketRecvConnector *o); + +/** + * Returns the output interface. + * The MTU of the interface will be as in {@link PacketRecvConnector_Init}. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * PacketRecvConnector_GetOutput (PacketRecvConnector *o); + +/** + * Connects input. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output input to connect. Its MTU must be <= MTU specified in + * {@link PacketRecvConnector_Init}. + */ +void PacketRecvConnector_ConnectInput (PacketRecvConnector *o, PacketRecvInterface *input); + +/** + * Disconnects input. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void PacketRecvConnector_DisconnectInput (PacketRecvConnector *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketRecvInterface.c b/external/badvpn_dns/flow/PacketRecvInterface.c new file mode 100644 index 00000000..40bb8c61 --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file PacketRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data); + return; +} + +void _PacketRecvInterface_job_done (PacketRecvInterface *i) +{ + ASSERT(i->state == PRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = PRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/external/badvpn_dns/flow/PacketRecvInterface.h b/external/badvpn_dns/flow/PacketRecvInterface.h new file mode 100644 index 00000000..6350ed24 --- /dev/null +++ b/external/badvpn_dns/flow/PacketRecvInterface.h @@ -0,0 +1,170 @@ +/** + * @file PacketRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a packet receiver to receive data packets from a packet sender. + */ + +#ifndef BADVPN_FLOW_PACKETRECVINTERFACE_H +#define BADVPN_FLOW_PACKETRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define PRI_STATE_NONE 1 +#define PRI_STATE_OPERATION_PENDING 2 +#define PRI_STATE_BUSY 3 +#define PRI_STATE_DONE_PENDING 4 + +typedef void (*PacketRecvInterface_handler_recv) (void *user, uint8_t *data); + +typedef void (*PacketRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + int mtu; + PacketRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + PacketRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} PacketRecvInterface; + +static void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void PacketRecvInterface_Free (PacketRecvInterface *i); + +static void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len); + +static int PacketRecvInterface_GetMTU (PacketRecvInterface *i); + +static void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user); + +static void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data); + +void _PacketRecvInterface_job_operation (PacketRecvInterface *i); +void _PacketRecvInterface_job_done (PacketRecvInterface *i); + +void PacketRecvInterface_Init (PacketRecvInterface *i, int mtu, PacketRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + i->mtu = mtu; + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_PacketRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_PacketRecvInterface_job_done, i); + + // set state + i->state = PRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void PacketRecvInterface_Free (PacketRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void PacketRecvInterface_Done (PacketRecvInterface *i, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= i->mtu) + ASSERT(i->state == PRI_STATE_BUSY) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = PRI_STATE_DONE_PENDING; +} + +int PacketRecvInterface_GetMTU (PacketRecvInterface *i) +{ + DebugObject_Access(&i->d_obj); + + return i->mtu; +} + +void PacketRecvInterface_Receiver_Init (PacketRecvInterface *i, PacketRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void PacketRecvInterface_Receiver_Recv (PacketRecvInterface *i, uint8_t *data) +{ + ASSERT(!(i->mtu > 0) || data) + ASSERT(i->state == PRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + BPending_Set(&i->job_operation); + + // set state + i->state = PRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/external/badvpn_dns/flow/PacketRouter.c b/external/badvpn_dns/flow/PacketRouter.c new file mode 100644 index 00000000..7b9f5f9e --- /dev/null +++ b/external/badvpn_dns/flow/PacketRouter.c @@ -0,0 +1,129 @@ +/** + * @file PacketRouter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +static void input_handler_done (PacketRouter *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->mtu - o->recv_offset) + ASSERT(!BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); + + // set next job + BPending_Set(&o->next_job); + + // call handler + o->handler(o->user, RouteBufferSource_Pointer(&o->rbs), data_len); + return; +} + +static void next_job_handler (PacketRouter *o) +{ + DebugObject_Access(&o->d_obj); + + // receive + PacketRecvInterface_Receiver_Recv(o->input, RouteBufferSource_Pointer(&o->rbs) + o->recv_offset); +} + +int PacketRouter_Init (PacketRouter *o, int mtu, int recv_offset, PacketRecvInterface *input, PacketRouter_handler handler, void *user, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + ASSERT(recv_offset >= 0) + ASSERT(recv_offset <= mtu) + ASSERT(PacketRecvInterface_GetMTU(input) <= mtu - recv_offset) + + // init arguments + o->mtu = mtu; + o->recv_offset = recv_offset; + o->input = input; + o->handler = handler; + o->user = user; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init RouteBufferSource + if (!RouteBufferSource_Init(&o->rbs, mtu)) { + goto fail0; + } + + // init next job + BPending_Init(&o->next_job, pg, (BPending_handler)next_job_handler, o); + + // receive + PacketRecvInterface_Receiver_Recv(o->input, RouteBufferSource_Pointer(&o->rbs) + o->recv_offset); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void PacketRouter_Free (PacketRouter *o) +{ + DebugObject_Free(&o->d_obj); + + // free next job + BPending_Free(&o->next_job); + + // free RouteBufferSource + RouteBufferSource_Free(&o->rbs); +} + +int PacketRouter_Route (PacketRouter *o, int len, RouteBuffer *output, uint8_t **next_buf, int copy_offset, int copy_len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->mtu) + ASSERT(RouteBuffer_GetMTU(output) == o->mtu) + ASSERT(copy_offset >= 0) + ASSERT(copy_offset <= o->mtu) + ASSERT(copy_len >= 0) + ASSERT(copy_len <= o->mtu - copy_offset) + ASSERT(BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); + + if (!RouteBufferSource_Route(&o->rbs, len, output, copy_offset, copy_len)) { + return 0; + } + + if (next_buf) { + *next_buf = RouteBufferSource_Pointer(&o->rbs); + } + + return 1; +} + +void PacketRouter_AssertRoute (PacketRouter *o) +{ + ASSERT(BPending_IsSet(&o->next_job)) + DebugObject_Access(&o->d_obj); +} diff --git a/external/badvpn_dns/flow/PacketRouter.h b/external/badvpn_dns/flow/PacketRouter.h new file mode 100644 index 00000000..3226078a --- /dev/null +++ b/external/badvpn_dns/flow/PacketRouter.h @@ -0,0 +1,126 @@ +/** + * @file PacketRouter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which simplifies routing packets to {@link RouteBuffer}'s from a + * {@link PacketRecvInterface} input. + */ + +#ifndef BADVPN_FLOW_PACKETROUTER_H +#define BADVPN_FLOW_PACKETROUTER_H + +#include +#include +#include +#include + +/** + * Handler called when a packet is received, allowing the user to route it + * to one or more buffers using {@link PacketRouter_Route}. + * + * @param user as in {@link PacketRouter_Init} + * @param buf the buffer for the packet. May be modified by the user. + * Will have space for mtu bytes. Only valid in the job context of + * this handler, until {@link PacketRouter_Route} is called successfully. + * @param recv_len length of the input packet (located at recv_offset bytes offset) + */ +typedef void (*PacketRouter_handler) (void *user, uint8_t *buf, int recv_len); + +/** + * Object which simplifies routing packets to {@link RouteBuffer}'s from a + * {@link PacketRecvInterface} input. + * + * Packets are routed by calling {@link PacketRouter_Route} (possibly multiple times) + * from the job context of the {@link PacketRouter_handler} handler. + */ +typedef struct { + int mtu; + int recv_offset; + PacketRecvInterface *input; + PacketRouter_handler handler; + void *user; + RouteBufferSource rbs; + BPending next_job; + DebugObject d_obj; +} PacketRouter; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. It will only be possible to route packets to + * {@link RouteBuffer}'s with the same MTU. + * @param recv_offset offset from the beginning for receiving input packets. + * Must be >=0 and <=mtu. The leading space should be initialized + * by the user before routing a packet. + * @param input input interface. Its MTU must be <= mtu - recv_offset. + * @param handler handler called when a packet is received to allow the user to route it + * @param user value passed to handler + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int PacketRouter_Init (PacketRouter *o, int mtu, int recv_offset, PacketRecvInterface *input, PacketRouter_handler handler, void *user, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void PacketRouter_Free (PacketRouter *o); + +/** + * Routes the current packet to the given buffer. + * Must be called from the job context of the {@link PacketRouter_handler} handler. + * On success, copies part of the current packet to next one (regardless if next_buf + * is provided or not; if not, copies before receiving another packet). + * + * @param o the object + * @param len total packet length (e.g. recv_offset + (recv_len from handler)). + * Must be >=0 and <=mtu. + * @param output buffer to route to. Its MTU must be the same as of this object. + * @param next_buf if not NULL, on success, will be set to the address of a new current + * packet that can be routed. The pointer will be valid in the job context of + * the calling handler, until this function is called successfully again + * (as for the original pointer provided by the handler). + * @param copy_offset Offset from the beginning for copying to the next packet. + * Must be >=0 and <=mtu. + * @param copy_len Number of bytes to copy from the old current + * packet to the next one. Must be >=0 and <= mtu - copy_offset. + * @return 1 on success, 0 on failure (buffer full) + */ +int PacketRouter_Route (PacketRouter *o, int len, RouteBuffer *output, uint8_t **next_buf, int copy_offset, int copy_len); + +/** + * Asserts that {@link PacketRouter_Route} can be called. + * + * @param o the object + */ +void PacketRouter_AssertRoute (PacketRouter *o); + +#endif diff --git a/external/badvpn_dns/flow/PacketStreamSender.c b/external/badvpn_dns/flow/PacketStreamSender.c new file mode 100644 index 00000000..153a72b9 --- /dev/null +++ b/external/badvpn_dns/flow/PacketStreamSender.c @@ -0,0 +1,111 @@ +/** + * @file PacketStreamSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void send_data (PacketStreamSender *s) +{ + ASSERT(s->in_len >= 0) + + if (s->in_used < s->in_len) { + // send more data + StreamPassInterface_Sender_Send(s->output, s->in + s->in_used, s->in_len - s->in_used); + } else { + // finish input packet + s->in_len = -1; + PacketPassInterface_Done(&s->input); + } +} + +static void input_handler_send (PacketStreamSender *s, uint8_t *data, int data_len) +{ + ASSERT(s->in_len == -1) + ASSERT(data_len >= 0) + DebugObject_Access(&s->d_obj); + + // set input packet + s->in_len = data_len; + s->in = data; + s->in_used = 0; + + // send + send_data(s); +} + +static void output_handler_done (PacketStreamSender *s, int data_len) +{ + ASSERT(s->in_len >= 0) + ASSERT(data_len > 0) + ASSERT(data_len <= s->in_len - s->in_used) + DebugObject_Access(&s->d_obj); + + // update number of bytes sent + s->in_used += data_len; + + // send + send_data(s); +} + +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg) +{ + ASSERT(mtu >= 0) + + // init arguments + s->output = output; + + // init input + PacketPassInterface_Init(&s->input, mtu, (PacketPassInterface_handler_send)input_handler_send, s, pg); + + // init output + StreamPassInterface_Sender_Init(s->output, (StreamPassInterface_handler_done)output_handler_done, s); + + // have no input packet + s->in_len = -1; + + DebugObject_Init(&s->d_obj); +} + +void PacketStreamSender_Free (PacketStreamSender *s) +{ + DebugObject_Free(&s->d_obj); + + // free input + PacketPassInterface_Free(&s->input); +} + +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s) +{ + DebugObject_Access(&s->d_obj); + + return &s->input; +} diff --git a/external/badvpn_dns/flow/PacketStreamSender.h b/external/badvpn_dns/flow/PacketStreamSender.h new file mode 100644 index 00000000..735360c7 --- /dev/null +++ b/external/badvpn_dns/flow/PacketStreamSender.h @@ -0,0 +1,83 @@ +/** + * @file PacketStreamSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ + +#ifndef BADVPN_FLOW_PACKETSTREAMSENDER_H +#define BADVPN_FLOW_PACKETSTREAMSENDER_H + +#include + +#include +#include +#include + +/** + * Object which forwards packets obtained with {@link PacketPassInterface} + * as a stream with {@link StreamPassInterface} (i.e. it concatenates them). + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface input; + StreamPassInterface *output; + int in_len; + uint8_t *in; + int in_used; +} PacketStreamSender; + +/** + * Initializes the object. + * + * @param s the object + * @param output output interface + * @param mtu input MTU. Must be >=0. + * @param pg pending group + */ +void PacketStreamSender_Init (PacketStreamSender *s, StreamPassInterface *output, int mtu, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param s the object + */ +void PacketStreamSender_Free (PacketStreamSender *s); + +/** + * Returns the input interface. + * Its MTU will be as in {@link PacketStreamSender_Init}. + * + * @param s the object + * @return input interface + */ +PacketPassInterface * PacketStreamSender_GetInput (PacketStreamSender *s); + +#endif diff --git a/external/badvpn_dns/flow/RouteBuffer.c b/external/badvpn_dns/flow/RouteBuffer.c new file mode 100644 index 00000000..dec7be4c --- /dev/null +++ b/external/badvpn_dns/flow/RouteBuffer.c @@ -0,0 +1,256 @@ +/** + * @file RouteBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include + +static struct RouteBuffer_packet * alloc_packet (int mtu) +{ + if (mtu > SIZE_MAX - sizeof(struct RouteBuffer_packet)) { + return NULL; + } + + // allocate memory + struct RouteBuffer_packet *p = (struct RouteBuffer_packet *)malloc(sizeof(*p) + mtu); + if (!p) { + return NULL; + } + + return p; +} + +static int alloc_free_packet (RouteBuffer *o) +{ + struct RouteBuffer_packet *p = alloc_packet(o->mtu); + if (!p) { + return 0; + } + + // add to free packets list + LinkedList1_Append(&o->packets_free, &p->node); + + return 1; +} + +static void free_free_packets (RouteBuffer *o) +{ + while (!LinkedList1_IsEmpty(&o->packets_free)) { + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetLast(&o->packets_free), struct RouteBuffer_packet, node); + + // remove from free packets list + LinkedList1_Remove(&o->packets_free, &p->node); + + // free memory + free(p); + } +} + +static void release_used_packet (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetFirst(&o->packets_used), struct RouteBuffer_packet, node); + + // remove from used packets list + LinkedList1_Remove(&o->packets_used, &p->node); + + // add to free packets list + LinkedList1_Append(&o->packets_free, &p->node); +} + +static void send_used_packet (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + + // get packet + struct RouteBuffer_packet *p = UPPER_OBJECT(LinkedList1_GetFirst(&o->packets_used), struct RouteBuffer_packet, node); + + // send + PacketPassInterface_Sender_Send(o->output, (uint8_t *)(p + 1), p->len); +} + +static void output_handler_done (RouteBuffer *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->packets_used)) + DebugObject_Access(&o->d_obj); + + // release packet + release_used_packet(o); + + // send next packet if there is one + if (!LinkedList1_IsEmpty(&o->packets_used)) { + send_used_packet(o); + } +} + +int RouteBuffer_Init (RouteBuffer *o, int mtu, PacketPassInterface *output, int buf_size) +{ + ASSERT(mtu >= 0) + ASSERT(PacketPassInterface_GetMTU(output) >= mtu) + ASSERT(buf_size > 0) + + // init arguments + o->mtu = mtu; + o->output = output; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init free packets list + LinkedList1_Init(&o->packets_free); + + // init used packets list + LinkedList1_Init(&o->packets_used); + + // allocate packets + for (int i = 0; i < buf_size; i++) { + if (!alloc_free_packet(o)) { + goto fail1; + } + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + free_free_packets(o); + return 0; +} + +void RouteBuffer_Free (RouteBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // release packets so they can be freed + while (!LinkedList1_IsEmpty(&o->packets_used)) { + release_used_packet(o); + } + + // free packets + free_free_packets(o); +} + +int RouteBuffer_GetMTU (RouteBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + return o->mtu; +} + +int RouteBufferSource_Init (RouteBufferSource *o, int mtu) +{ + ASSERT(mtu >= 0) + + // init arguments + o->mtu = mtu; + + // allocate current packet + if (!(o->current_packet = alloc_packet(o->mtu))) { + goto fail0; + } + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void RouteBufferSource_Free (RouteBufferSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free current packet + free(o->current_packet); +} + +uint8_t * RouteBufferSource_Pointer (RouteBufferSource *o) +{ + DebugObject_Access(&o->d_obj); + + return (uint8_t *)(o->current_packet + 1); +} + +int RouteBufferSource_Route (RouteBufferSource *o, int len, RouteBuffer *b, int copy_offset, int copy_len) +{ + ASSERT(len >= 0) + ASSERT(len <= o->mtu) + ASSERT(b->mtu == o->mtu) + ASSERT(copy_offset >= 0) + ASSERT(copy_offset <= o->mtu) + ASSERT(copy_len >= 0) + ASSERT(copy_len <= o->mtu - copy_offset) + DebugObject_Access(&b->d_obj); + DebugObject_Access(&o->d_obj); + + // check if there's space in the buffer + if (LinkedList1_IsEmpty(&b->packets_free)) { + return 0; + } + + int was_empty = LinkedList1_IsEmpty(&b->packets_used); + + struct RouteBuffer_packet *p = o->current_packet; + + // set packet length + p->len = len; + + // append packet to used packets list + LinkedList1_Append(&b->packets_used, &p->node); + + // get a free packet + struct RouteBuffer_packet *np = UPPER_OBJECT(LinkedList1_GetLast(&b->packets_free), struct RouteBuffer_packet, node); + + // remove it from free packets list + LinkedList1_Remove(&b->packets_free, &np->node); + + // make it the current packet + o->current_packet = np; + + // copy packet + if (copy_len > 0) { + memcpy((uint8_t *)(np + 1) + copy_offset, (uint8_t *)(p + 1) + copy_offset, copy_len); + } + + // start sending if required + if (was_empty) { + send_used_packet(b); + } + + return 1; +} diff --git a/external/badvpn_dns/flow/RouteBuffer.h b/external/badvpn_dns/flow/RouteBuffer.h new file mode 100644 index 00000000..d0f7b415 --- /dev/null +++ b/external/badvpn_dns/flow/RouteBuffer.h @@ -0,0 +1,139 @@ +/** + * @file RouteBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer for zero-copy packet routing. + */ + +#ifndef BADVPN_FLOW_ROUTEBUFFER_H +#define BADVPN_FLOW_ROUTEBUFFER_H + +#include +#include +#include +#include + +struct RouteBuffer_packet { + LinkedList1Node node; + int len; +}; + +/** + * Packet buffer for zero-copy packet routing. + * + * Packets are buffered using {@link RouteBufferSource} objects. + */ +typedef struct { + int mtu; + PacketPassInterface *output; + LinkedList1 packets_free; + LinkedList1 packets_used; + DebugObject d_obj; +} RouteBuffer; + +/** + * Object through which packets are buffered into {@link RouteBuffer} objects. + * + * A packet is routed by calling {@link RouteBufferSource_Pointer}, writing it to + * the returned address, then calling {@link RouteBufferSource_Route}. + */ +typedef struct { + int mtu; + struct RouteBuffer_packet *current_packet; + DebugObject d_obj; +} RouteBufferSource; + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. It will only be possible to route packets to this buffer + * from {@link RouteBufferSource}.s with the same MTU. + * @param output output interface. Its MTU must be >=mtu. + * @param buf_size size of the buffer in number of packet. Must be >0. + * @return 1 on success, 0 on failure + */ +int RouteBuffer_Init (RouteBuffer *o, int mtu, PacketPassInterface *output, int buf_size) WARN_UNUSED; + +/** + * Frees the object. + */ +void RouteBuffer_Free (RouteBuffer *o); + +/** + * Retuns the buffer's MTU (mtu argument to {@link RouteBuffer_Init}). + * + * @return MTU + */ +int RouteBuffer_GetMTU (RouteBuffer *o); + +/** + * Initializes the object. + * + * @param o the object + * @param mtu maximum packet size. Must be >=0. The object will only be able to route packets + * to {@link RouteBuffer}'s with the same MTU. + * @return 1 on success, 0 on failure + */ +int RouteBufferSource_Init (RouteBufferSource *o, int mtu) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void RouteBufferSource_Free (RouteBufferSource *o); + +/** + * Returns a pointer to the current packet. + * The pointed to memory area will have space for MTU bytes. + * The pointer is only valid until {@link RouteBufferSource_Route} succeeds. + * + * @param o the object + * @return pointer to the current packet + */ +uint8_t * RouteBufferSource_Pointer (RouteBufferSource *o); + +/** + * Routes the current packet to a given buffer. + * On success, this invalidates the pointer previously returned from + * {@link RouteBufferSource_Pointer}. + * + * @param o the object + * @param len length of the packet. Must be >=0 and <=MTU. + * @param b buffer to route to. Its MTU must equal this object's MTU. + * @param copy_offset Offset from the beginning for copying. Must be >=0 and + * <=mtu. + * @param copy_len Number of bytes to copy from the old current packet to the new one. + * Must be >=0 and <= mtu - copy_offset. + * @return 1 on success, 0 on failure + */ +int RouteBufferSource_Route (RouteBufferSource *o, int len, RouteBuffer *b, int copy_offset, int copy_len); + +#endif diff --git a/external/badvpn_dns/flow/SinglePacketBuffer.c b/external/badvpn_dns/flow/SinglePacketBuffer.c new file mode 100644 index 00000000..bbc72aed --- /dev/null +++ b/external/badvpn_dns/flow/SinglePacketBuffer.c @@ -0,0 +1,87 @@ +/** + * @file SinglePacketBuffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +static void input_handler_done (SinglePacketBuffer *o, int in_len) +{ + DebugObject_Access(&o->d_obj); + + PacketPassInterface_Sender_Send(o->output, o->buf, in_len); +} + +static void output_handler_done (SinglePacketBuffer *o) +{ + DebugObject_Access(&o->d_obj); + + PacketRecvInterface_Receiver_Recv(o->input, o->buf); +} + +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) >= PacketRecvInterface_GetMTU(input)) + + // init arguments + o->input = input; + o->output = output; + + // init input + PacketRecvInterface_Receiver_Init(o->input, (PacketRecvInterface_handler_done)input_handler_done, o); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init buffer + if (!(o->buf = (uint8_t *)BAlloc(PacketRecvInterface_GetMTU(o->input)))) { + goto fail1; + } + + // schedule receive + PacketRecvInterface_Receiver_Recv(o->input, o->buf); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + return 0; +} + +void SinglePacketBuffer_Free (SinglePacketBuffer *o) +{ + DebugObject_Free(&o->d_obj); + + // free buffer + BFree(o->buf); +} diff --git a/external/badvpn_dns/flow/SinglePacketBuffer.h b/external/badvpn_dns/flow/SinglePacketBuffer.h new file mode 100644 index 00000000..87314a5c --- /dev/null +++ b/external/badvpn_dns/flow/SinglePacketBuffer.h @@ -0,0 +1,75 @@ +/** + * @file SinglePacketBuffer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ + +#ifndef BADVPN_FLOW_SINGLEPACKETBUFFER_H +#define BADVPN_FLOW_SINGLEPACKETBUFFER_H + +#include + +#include +#include +#include +#include + +/** + * Packet buffer with {@link PacketRecvInterface} input and {@link PacketPassInterface} output + * than can store only a single packet. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface *input; + PacketPassInterface *output; + uint8_t *buf; +} SinglePacketBuffer; + +/** + * Initializes the object. + * Output MTU must be >= input MTU. + * + * @param o the object + * @param input input interface + * @param output output interface + * @param pg pending group + * @return 1 on success, 0 on failure + */ +int SinglePacketBuffer_Init (SinglePacketBuffer *o, PacketRecvInterface *input, PacketPassInterface *output, BPendingGroup *pg) WARN_UNUSED; + +/** + * Frees the object + * + * @param o the object + */ +void SinglePacketBuffer_Free (SinglePacketBuffer *o); + +#endif diff --git a/external/badvpn_dns/flow/SinglePacketSender.c b/external/badvpn_dns/flow/SinglePacketSender.c new file mode 100644 index 00000000..b1f3ec7e --- /dev/null +++ b/external/badvpn_dns/flow/SinglePacketSender.c @@ -0,0 +1,72 @@ +/** + * @file SinglePacketSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void call_handler (SinglePacketSender *o) +{ + DEBUGERROR(&o->d_err, o->handler(o->user)); +} + +static void output_handler_done (SinglePacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + // notify user + call_handler(o); + return; +} + +void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg) +{ + ASSERT(packet_len >= 0) + ASSERT(packet_len <= PacketPassInterface_GetMTU(output)) + + // init arguments + o->output = output; + o->handler = handler; + o->user = user; + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // schedule send + PacketPassInterface_Sender_Send(o->output, packet, packet_len); + + DebugObject_Init(&o->d_obj); + DebugError_Init(&o->d_err, pg); +} + +void SinglePacketSender_Free (SinglePacketSender *o) +{ + DebugError_Free(&o->d_err); + DebugObject_Free(&o->d_obj); +} diff --git a/external/badvpn_dns/flow/SinglePacketSender.h b/external/badvpn_dns/flow/SinglePacketSender.h new file mode 100644 index 00000000..c9289d8f --- /dev/null +++ b/external/badvpn_dns/flow/SinglePacketSender.h @@ -0,0 +1,82 @@ +/** + * @file SinglePacketSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} source which sends a single packet. + */ + +#ifndef BADVPN_FLOW_SINGLEPACKETSENDER_H +#define BADVPN_FLOW_SINGLEPACKETSENDER_H + +#include + +#include +#include +#include + +/** + * Handler function called after the packet is sent. + * The object must be freed from within this handler. + * + * @param user as in {@link SinglePacketSender_Init}. + */ +typedef void (*SinglePacketSender_handler) (void *user); + +/** + * A {@link PacketPassInterface} source which sends a single packet. + */ +typedef struct { + PacketPassInterface *output; + SinglePacketSender_handler handler; + void *user; + DebugObject d_obj; + DebugError d_err; +} SinglePacketSender; + +/** + * Initializes the object. + * + * @param o the object + * @param packet packet to be sent. Must be available as long as the object exists. + * @param packet_len length of the packet. Must be >=0 and <=(MTU of output). + * @param output output interface + * @param handler handler to call when the packet is sent + * @param user value to pass to handler + * @param pg pending group + */ +void SinglePacketSender_Init (SinglePacketSender *o, uint8_t *packet, int packet_len, PacketPassInterface *output, SinglePacketSender_handler handler, void *user, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SinglePacketSender_Free (SinglePacketSender *o); + +#endif diff --git a/external/badvpn_dns/flow/SingleStreamReceiver.c b/external/badvpn_dns/flow/SingleStreamReceiver.c new file mode 100644 index 00000000..b4632908 --- /dev/null +++ b/external/badvpn_dns/flow/SingleStreamReceiver.c @@ -0,0 +1,82 @@ +/** + * @file SingleStreamReceiver.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "SingleStreamReceiver.h" + +static void input_handler_done (SingleStreamReceiver *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->packet_len - o->pos) + + // update position + o->pos += data_len; + + // if everything was received, notify user + if (o->pos == o->packet_len) { + DEBUGERROR(&o->d_err, o->handler(o->user)); + return; + } + + // receive more + StreamRecvInterface_Receiver_Recv(o->input, o->packet + o->pos, o->packet_len - o->pos); +} + +void SingleStreamReceiver_Init (SingleStreamReceiver *o, uint8_t *packet, int packet_len, StreamRecvInterface *input, BPendingGroup *pg, void *user, SingleStreamReceiver_handler handler) +{ + ASSERT(packet_len > 0) + ASSERT(handler) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + o->input = input; + o->user = user; + o->handler = handler; + + // set position zero + o->pos = 0; + + // init output + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->packet + o->pos, o->packet_len - o->pos); + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); +} + +void SingleStreamReceiver_Free (SingleStreamReceiver *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +} diff --git a/external/badvpn_dns/flow/SingleStreamReceiver.h b/external/badvpn_dns/flow/SingleStreamReceiver.h new file mode 100644 index 00000000..c9c6219e --- /dev/null +++ b/external/badvpn_dns/flow/SingleStreamReceiver.h @@ -0,0 +1,53 @@ +/** + * @file SingleStreamReceiver.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLESTREAMRECEIVER_H +#define BADVPN_SINGLESTREAMRECEIVER_H + +#include +#include +#include + +typedef void (*SingleStreamReceiver_handler) (void *user); + +typedef struct { + uint8_t *packet; + int packet_len; + StreamRecvInterface *input; + void *user; + SingleStreamReceiver_handler handler; + int pos; + DebugError d_err; + DebugObject d_obj; +} SingleStreamReceiver; + +void SingleStreamReceiver_Init (SingleStreamReceiver *o, uint8_t *packet, int packet_len, StreamRecvInterface *input, BPendingGroup *pg, void *user, SingleStreamReceiver_handler handler); +void SingleStreamReceiver_Free (SingleStreamReceiver *o); + +#endif diff --git a/external/badvpn_dns/flow/SingleStreamSender.c b/external/badvpn_dns/flow/SingleStreamSender.c new file mode 100644 index 00000000..eb333069 --- /dev/null +++ b/external/badvpn_dns/flow/SingleStreamSender.c @@ -0,0 +1,82 @@ +/** + * @file SingleStreamSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "SingleStreamSender.h" + +static void output_handler_done (SingleStreamSender *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + ASSERT(data_len <= o->packet_len - o->pos) + + // update position + o->pos += data_len; + + // if everything was sent, notify user + if (o->pos == o->packet_len) { + DEBUGERROR(&o->d_err, o->handler(o->user)); + return; + } + + // send more + StreamPassInterface_Sender_Send(o->output, o->packet + o->pos, o->packet_len - o->pos); +} + +void SingleStreamSender_Init (SingleStreamSender *o, uint8_t *packet, int packet_len, StreamPassInterface *output, BPendingGroup *pg, void *user, SingleStreamSender_handler handler) +{ + ASSERT(packet_len > 0) + ASSERT(handler) + + // init arguments + o->packet = packet; + o->packet_len = packet_len; + o->output = output; + o->user = user; + o->handler = handler; + + // set position zero + o->pos = 0; + + // init output + StreamPassInterface_Sender_Init(o->output, (StreamPassInterface_handler_done)output_handler_done, o); + + // start sending + StreamPassInterface_Sender_Send(o->output, o->packet + o->pos, o->packet_len - o->pos); + + DebugError_Init(&o->d_err, pg); + DebugObject_Init(&o->d_obj); +} + +void SingleStreamSender_Free (SingleStreamSender *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +} diff --git a/external/badvpn_dns/flow/SingleStreamSender.h b/external/badvpn_dns/flow/SingleStreamSender.h new file mode 100644 index 00000000..180a9bfa --- /dev/null +++ b/external/badvpn_dns/flow/SingleStreamSender.h @@ -0,0 +1,53 @@ +/** + * @file SingleStreamSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SINGLESTREAMSENDER_H +#define BADVPN_SINGLESTREAMSENDER_H + +#include +#include +#include + +typedef void (*SingleStreamSender_handler) (void *user); + +typedef struct { + uint8_t *packet; + int packet_len; + StreamPassInterface *output; + void *user; + SingleStreamSender_handler handler; + int pos; + DebugError d_err; + DebugObject d_obj; +} SingleStreamSender; + +void SingleStreamSender_Init (SingleStreamSender *o, uint8_t *packet, int packet_len, StreamPassInterface *output, BPendingGroup *pg, void *user, SingleStreamSender_handler handler); +void SingleStreamSender_Free (SingleStreamSender *o); + +#endif diff --git a/external/badvpn_dns/flow/StreamPacketSender.c b/external/badvpn_dns/flow/StreamPacketSender.c new file mode 100644 index 00000000..1e0a949b --- /dev/null +++ b/external/badvpn_dns/flow/StreamPacketSender.c @@ -0,0 +1,90 @@ +/** + * @file StreamPacketSender.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "StreamPacketSender.h" + +static void input_handler_send (StreamPacketSender *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(data_len > 0) + + // limit length to MTU and remember + if (data_len > o->output_mtu) { + o->sending_len = o->output_mtu; + } else { + o->sending_len = data_len; + } + + // send + PacketPassInterface_Sender_Send(o->output, data, o->sending_len); +} + +static void output_handler_done (StreamPacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + // done + StreamPassInterface_Done(&o->input, o->sending_len); +} + +void StreamPacketSender_Init (StreamPacketSender *o, PacketPassInterface *output, BPendingGroup *pg) +{ + ASSERT(PacketPassInterface_GetMTU(output) > 0) + + // init arguments + o->output = output; + + // remember output MTU + o->output_mtu = PacketPassInterface_GetMTU(output); + + // init input + StreamPassInterface_Init(&o->input, (StreamPassInterface_handler_send)input_handler_send, o, pg); + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + DebugObject_Init(&o->d_obj); +} + +void StreamPacketSender_Free (StreamPacketSender *o) +{ + DebugObject_Free(&o->d_obj); + + // free input + StreamPassInterface_Free(&o->input); +} + +StreamPassInterface * StreamPacketSender_GetInput (StreamPacketSender *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} diff --git a/external/badvpn_dns/flow/StreamPacketSender.h b/external/badvpn_dns/flow/StreamPacketSender.h new file mode 100644 index 00000000..19bda5ee --- /dev/null +++ b/external/badvpn_dns/flow/StreamPacketSender.h @@ -0,0 +1,77 @@ +/** + * @file StreamPacketSender.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STREAMPACKETSENDER_H +#define BADVPN_STREAMPACKETSENDER_H + +#include +#include +#include + +/** + * Object which breaks an input stream into output packets. The resulting + * packets will have positive length, and, when concatenated, will form the + * original stream. + * + * Input is with {@link StreamPassInterface}. + * Output is with {@link PacketPassInterface}. + */ +typedef struct { + PacketPassInterface *output; + int output_mtu; + StreamPassInterface input; + int sending_len; + DebugObject d_obj; +} StreamPacketSender; + +/** + * Initializes the object. + * + * @param o the object + * @param output output interface. Its MTU must be >0. + * @param pg pending group we live in + */ +void StreamPacketSender_Init (StreamPacketSender *o, PacketPassInterface *output, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamPacketSender_Free (StreamPacketSender *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +StreamPassInterface * StreamPacketSender_GetInput (StreamPacketSender *o); + +#endif diff --git a/external/badvpn_dns/flow/StreamPassConnector.c b/external/badvpn_dns/flow/StreamPassConnector.c new file mode 100644 index 00000000..d3075c73 --- /dev/null +++ b/external/badvpn_dns/flow/StreamPassConnector.c @@ -0,0 +1,120 @@ +/** + * @file StreamPassConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void input_handler_send (StreamPassConnector *o, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(o->in_len == -1) + DebugObject_Access(&o->d_obj); + + // remember input packet + o->in_len = data_len; + o->in = data; + + if (o->output) { + // schedule send + StreamPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +static void output_handler_done (StreamPassConnector *o, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= o->in_len) + ASSERT(o->in_len > 0) + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // have no input packet + o->in_len = -1; + + // allow input to send more packets + StreamPassInterface_Done(&o->input, data_len); +} + +void StreamPassConnector_Init (StreamPassConnector *o, BPendingGroup *pg) +{ + // init output + StreamPassInterface_Init(&o->input, (StreamPassInterface_handler_send)input_handler_send, o, pg); + + // have no input packet + o->in_len = -1; + + // have no output + o->output = NULL; + + DebugObject_Init(&o->d_obj); +} + +void StreamPassConnector_Free (StreamPassConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + StreamPassInterface_Free(&o->input); +} + +StreamPassInterface * StreamPassConnector_GetInput (StreamPassConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void StreamPassConnector_ConnectOutput (StreamPassConnector *o, StreamPassInterface *output) +{ + ASSERT(!o->output) + DebugObject_Access(&o->d_obj); + + // set output + o->output = output; + + // init output + StreamPassInterface_Sender_Init(o->output, (StreamPassInterface_handler_done)output_handler_done, o); + + // if we have an input packet, schedule send + if (o->in_len > 0) { + StreamPassInterface_Sender_Send(o->output, o->in, o->in_len); + } +} + +void StreamPassConnector_DisconnectOutput (StreamPassConnector *o) +{ + ASSERT(o->output) + DebugObject_Access(&o->d_obj); + + // set no output + o->output = NULL; +} diff --git a/external/badvpn_dns/flow/StreamPassConnector.h b/external/badvpn_dns/flow/StreamPassConnector.h new file mode 100644 index 00000000..aa791957 --- /dev/null +++ b/external/badvpn_dns/flow/StreamPassConnector.h @@ -0,0 +1,98 @@ +/** + * @file StreamPassConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link StreamPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_STREAMPASSCONNECTOR_H +#define BADVPN_FLOW_STREAMPASSCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link StreamPassInterface} layer which allows the output to be + * connected and disconnected on the fly. + */ +typedef struct { + StreamPassInterface input; + int in_len; + uint8_t *in; + StreamPassInterface *output; + DebugObject d_obj; +} StreamPassConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param pg pending group + */ +void StreamPassConnector_Init (StreamPassConnector *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamPassConnector_Free (StreamPassConnector *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +StreamPassInterface * StreamPassConnector_GetInput (StreamPassConnector *o); + +/** + * Connects output. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output output to connect + */ +void StreamPassConnector_ConnectOutput (StreamPassConnector *o, StreamPassInterface *output); + +/** + * Disconnects output. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void StreamPassConnector_DisconnectOutput (StreamPassConnector *o); + +#endif diff --git a/external/badvpn_dns/flow/StreamPassInterface.c b/external/badvpn_dns/flow/StreamPassInterface.c new file mode 100644 index 00000000..f0dc5478 --- /dev/null +++ b/external/badvpn_dns/flow/StreamPassInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamPassInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamPassInterface_job_operation (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamPassInterface_job_done (StreamPassInterface *i) +{ + ASSERT(i->state == SPI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SPI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/external/badvpn_dns/flow/StreamPassInterface.h b/external/badvpn_dns/flow/StreamPassInterface.h new file mode 100644 index 00000000..1fe650e2 --- /dev/null +++ b/external/badvpn_dns/flow/StreamPassInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamPassInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream sender to pass stream data to a stream receiver. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamRecvInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamRecvInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMPASSINTERFACE_H +#define BADVPN_FLOW_STREAMPASSINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SPI_STATE_NONE 1 +#define SPI_STATE_OPERATION_PENDING 2 +#define SPI_STATE_BUSY 3 +#define SPI_STATE_DONE_PENDING 4 + +typedef void (*StreamPassInterface_handler_send) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamPassInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamPassInterface_handler_send handler_operation; + void *user_provider; + + // user data + StreamPassInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamPassInterface; + +static void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg); + +static void StreamPassInterface_Free (StreamPassInterface *i); + +static void StreamPassInterface_Done (StreamPassInterface *i, int data_len); + +static void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user); + +static void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len); + +void _StreamPassInterface_job_operation (StreamPassInterface *i); +void _StreamPassInterface_job_done (StreamPassInterface *i); + +void StreamPassInterface_Init (StreamPassInterface *i, StreamPassInterface_handler_send handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamPassInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamPassInterface_job_done, i); + + // set state + i->state = SPI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamPassInterface_Free (StreamPassInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamPassInterface_Done (StreamPassInterface *i, int data_len) +{ + ASSERT(i->state == SPI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SPI_STATE_DONE_PENDING; +} + +void StreamPassInterface_Sender_Init (StreamPassInterface *i, StreamPassInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamPassInterface_Sender_Send (StreamPassInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SPI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SPI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/external/badvpn_dns/flow/StreamRecvConnector.c b/external/badvpn_dns/flow/StreamRecvConnector.c new file mode 100644 index 00000000..beb6a882 --- /dev/null +++ b/external/badvpn_dns/flow/StreamRecvConnector.c @@ -0,0 +1,120 @@ +/** + * @file StreamRecvConnector.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void output_handler_recv (StreamRecvConnector *o, uint8_t *data, int data_avail) +{ + ASSERT(data_avail > 0) + ASSERT(o->out_avail == -1) + DebugObject_Access(&o->d_obj); + + // remember output packet + o->out_avail = data_avail; + o->out = data; + + if (o->input) { + // schedule receive + StreamRecvInterface_Receiver_Recv(o->input, o->out, o->out_avail); + } +} + +static void input_handler_done (StreamRecvConnector *o, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= o->out_avail) + ASSERT(o->out_avail > 0) + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // have no output packet + o->out_avail = -1; + + // allow output to receive more packets + StreamRecvInterface_Done(&o->output, data_len); +} + +void StreamRecvConnector_Init (StreamRecvConnector *o, BPendingGroup *pg) +{ + // init output + StreamRecvInterface_Init(&o->output, (StreamRecvInterface_handler_recv)output_handler_recv, o, pg); + + // have no output packet + o->out_avail = -1; + + // have no input + o->input = NULL; + + DebugObject_Init(&o->d_obj); +} + +void StreamRecvConnector_Free (StreamRecvConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + StreamRecvInterface_Free(&o->output); +} + +StreamRecvInterface * StreamRecvConnector_GetOutput (StreamRecvConnector *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} + +void StreamRecvConnector_ConnectInput (StreamRecvConnector *o, StreamRecvInterface *input) +{ + ASSERT(!o->input) + DebugObject_Access(&o->d_obj); + + // set input + o->input = input; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // if we have an output packet, schedule receive + if (o->out_avail > 0) { + StreamRecvInterface_Receiver_Recv(o->input, o->out, o->out_avail); + } +} + +void StreamRecvConnector_DisconnectInput (StreamRecvConnector *o) +{ + ASSERT(o->input) + DebugObject_Access(&o->d_obj); + + // set no input + o->input = NULL; +} diff --git a/external/badvpn_dns/flow/StreamRecvConnector.h b/external/badvpn_dns/flow/StreamRecvConnector.h new file mode 100644 index 00000000..ed111ce3 --- /dev/null +++ b/external/badvpn_dns/flow/StreamRecvConnector.h @@ -0,0 +1,98 @@ +/** + * @file StreamRecvConnector.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link StreamRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ + +#ifndef BADVPN_FLOW_STREAMRECVCONNECTOR_H +#define BADVPN_FLOW_STREAMRECVCONNECTOR_H + +#include + +#include +#include + +/** + * A {@link StreamRecvInterface} layer which allows the input to be + * connected and disconnected on the fly. + */ +typedef struct { + StreamRecvInterface output; + int out_avail; + uint8_t *out; + StreamRecvInterface *input; + DebugObject d_obj; +} StreamRecvConnector; + +/** + * Initializes the object. + * The object is initialized in not connected state. + * + * @param o the object + * @param pg pending group + */ +void StreamRecvConnector_Init (StreamRecvConnector *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void StreamRecvConnector_Free (StreamRecvConnector *o); + +/** + * Returns the output interface. + * + * @param o the object + * @return output interface + */ +StreamRecvInterface * StreamRecvConnector_GetOutput (StreamRecvConnector *o); + +/** + * Connects input. + * The object must be in not connected state. + * The object enters connected state. + * + * @param o the object + * @param output input to connect + */ +void StreamRecvConnector_ConnectInput (StreamRecvConnector *o, StreamRecvInterface *input); + +/** + * Disconnects input. + * The object must be in connected state. + * The object enters not connected state. + * + * @param o the object + */ +void StreamRecvConnector_DisconnectInput (StreamRecvConnector *o); + +#endif diff --git a/external/badvpn_dns/flow/StreamRecvInterface.c b/external/badvpn_dns/flow/StreamRecvInterface.c new file mode 100644 index 00000000..697e1ae6 --- /dev/null +++ b/external/badvpn_dns/flow/StreamRecvInterface.c @@ -0,0 +1,56 @@ +/** + * @file StreamRecvInterface.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_OPERATION_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_BUSY; + + // call handler + i->handler_operation(i->user_provider, i->job_operation_data, i->job_operation_len); + return; +} + +void _StreamRecvInterface_job_done (StreamRecvInterface *i) +{ + ASSERT(i->state == SRI_STATE_DONE_PENDING) + DebugObject_Access(&i->d_obj); + + // set state + i->state = SRI_STATE_NONE; + + // call handler + i->handler_done(i->user_user, i->job_done_len); + return; +} diff --git a/external/badvpn_dns/flow/StreamRecvInterface.h b/external/badvpn_dns/flow/StreamRecvInterface.h new file mode 100644 index 00000000..cd4a1813 --- /dev/null +++ b/external/badvpn_dns/flow/StreamRecvInterface.h @@ -0,0 +1,165 @@ +/** + * @file StreamRecvInterface.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Interface allowing a stream receiver to receive stream data from a stream sender. + * + * Note that this interface behaves exactly the same and has the same code as + * {@link StreamPassInterface} if names and its external semantics are disregarded. + * If you modify this file, you should probably modify {@link StreamPassInterface} + * too. + */ + +#ifndef BADVPN_FLOW_STREAMRECVINTERFACE_H +#define BADVPN_FLOW_STREAMRECVINTERFACE_H + +#include +#include + +#include +#include +#include + +#define SRI_STATE_NONE 1 +#define SRI_STATE_OPERATION_PENDING 2 +#define SRI_STATE_BUSY 3 +#define SRI_STATE_DONE_PENDING 4 + +typedef void (*StreamRecvInterface_handler_recv) (void *user, uint8_t *data, int data_len); + +typedef void (*StreamRecvInterface_handler_done) (void *user, int data_len); + +typedef struct { + // provider data + StreamRecvInterface_handler_recv handler_operation; + void *user_provider; + + // user data + StreamRecvInterface_handler_done handler_done; + void *user_user; + + // operation job + BPending job_operation; + uint8_t *job_operation_data; + int job_operation_len; + + // done job + BPending job_done; + int job_done_len; + + // state + int state; + + DebugObject d_obj; +} StreamRecvInterface; + +static void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg); + +static void StreamRecvInterface_Free (StreamRecvInterface *i); + +static void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len); + +static void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user); + +static void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len); + +void _StreamRecvInterface_job_operation (StreamRecvInterface *i); +void _StreamRecvInterface_job_done (StreamRecvInterface *i); + +void StreamRecvInterface_Init (StreamRecvInterface *i, StreamRecvInterface_handler_recv handler_operation, void *user, BPendingGroup *pg) +{ + // init arguments + i->handler_operation = handler_operation; + i->user_provider = user; + + // set no user + i->handler_done = NULL; + + // init jobs + BPending_Init(&i->job_operation, pg, (BPending_handler)_StreamRecvInterface_job_operation, i); + BPending_Init(&i->job_done, pg, (BPending_handler)_StreamRecvInterface_job_done, i); + + // set state + i->state = SRI_STATE_NONE; + + DebugObject_Init(&i->d_obj); +} + +void StreamRecvInterface_Free (StreamRecvInterface *i) +{ + DebugObject_Free(&i->d_obj); + + // free jobs + BPending_Free(&i->job_done); + BPending_Free(&i->job_operation); +} + +void StreamRecvInterface_Done (StreamRecvInterface *i, int data_len) +{ + ASSERT(i->state == SRI_STATE_BUSY) + ASSERT(data_len > 0) + ASSERT(data_len <= i->job_operation_len) + DebugObject_Access(&i->d_obj); + + // schedule done + i->job_done_len = data_len; + BPending_Set(&i->job_done); + + // set state + i->state = SRI_STATE_DONE_PENDING; +} + +void StreamRecvInterface_Receiver_Init (StreamRecvInterface *i, StreamRecvInterface_handler_done handler_done, void *user) +{ + ASSERT(handler_done) + ASSERT(!i->handler_done) + DebugObject_Access(&i->d_obj); + + i->handler_done = handler_done; + i->user_user = user; +} + +void StreamRecvInterface_Receiver_Recv (StreamRecvInterface *i, uint8_t *data, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data) + ASSERT(i->state == SRI_STATE_NONE) + ASSERT(i->handler_done) + DebugObject_Access(&i->d_obj); + + // schedule operation + i->job_operation_data = data; + i->job_operation_len = data_len; + BPending_Set(&i->job_operation); + + // set state + i->state = SRI_STATE_OPERATION_PENDING; +} + +#endif diff --git a/external/badvpn_dns/flowextra/CMakeLists.txt b/external/badvpn_dns/flowextra/CMakeLists.txt new file mode 100644 index 00000000..6ceb1c0a --- /dev/null +++ b/external/badvpn_dns/flowextra/CMakeLists.txt @@ -0,0 +1,5 @@ +set(FLOWEXTRA_SOURCES + PacketPassInactivityMonitor.c + KeepaliveIO.c +) +badvpn_add_library(flowextra "flow;system" "" "${FLOWEXTRA_SOURCES}") diff --git a/external/badvpn_dns/flowextra/KeepaliveIO.c b/external/badvpn_dns/flowextra/KeepaliveIO.c new file mode 100644 index 00000000..4e803663 --- /dev/null +++ b/external/badvpn_dns/flowextra/KeepaliveIO.c @@ -0,0 +1,112 @@ +/** + * @file KeepaliveIO.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "KeepaliveIO.h" + +static void keepalive_handler (KeepaliveIO *o) +{ + DebugObject_Access(&o->d_obj); + + PacketRecvBlocker_AllowBlockedPacket(&o->ka_blocker); +} + +int KeepaliveIO_Init (KeepaliveIO *o, BReactor *reactor, PacketPassInterface *output, PacketRecvInterface *keepalive_input, btime_t keepalive_interval_ms) +{ + ASSERT(PacketRecvInterface_GetMTU(keepalive_input) <= PacketPassInterface_GetMTU(output)) + ASSERT(keepalive_interval_ms > 0) + + // set arguments + o->reactor = reactor; + + // init keep-alive sender + PacketPassInactivityMonitor_Init(&o->kasender, output, o->reactor, keepalive_interval_ms, (PacketPassInactivityMonitor_handler)keepalive_handler, o); + + // init queue + PacketPassPriorityQueue_Init(&o->queue, PacketPassInactivityMonitor_GetInput(&o->kasender), BReactor_PendingGroup(o->reactor), 0); + + // init keepalive flow + PacketPassPriorityQueueFlow_Init(&o->ka_qflow, &o->queue, -1); + + // init keepalive blocker + PacketRecvBlocker_Init(&o->ka_blocker, keepalive_input, BReactor_PendingGroup(reactor)); + + // init keepalive buffer + if (!SinglePacketBuffer_Init(&o->ka_buffer, PacketRecvBlocker_GetOutput(&o->ka_blocker), PacketPassPriorityQueueFlow_GetInput(&o->ka_qflow), BReactor_PendingGroup(o->reactor))) { + goto fail1; + } + + // init user flow + PacketPassPriorityQueueFlow_Init(&o->user_qflow, &o->queue, 0); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail1: + PacketRecvBlocker_Free(&o->ka_blocker); + PacketPassPriorityQueueFlow_Free(&o->ka_qflow); + PacketPassPriorityQueue_Free(&o->queue); + PacketPassInactivityMonitor_Free(&o->kasender); + return 0; +} + +void KeepaliveIO_Free (KeepaliveIO *o) +{ + DebugObject_Free(&o->d_obj); + + // allow freeing queue flows + PacketPassPriorityQueue_PrepareFree(&o->queue); + + // free user flow + PacketPassPriorityQueueFlow_Free(&o->user_qflow); + + // free keepalive buffer + SinglePacketBuffer_Free(&o->ka_buffer); + + // free keepalive blocker + PacketRecvBlocker_Free(&o->ka_blocker); + + // free keepalive flow + PacketPassPriorityQueueFlow_Free(&o->ka_qflow); + + // free queue + PacketPassPriorityQueue_Free(&o->queue); + + // free keep-alive sender + PacketPassInactivityMonitor_Free(&o->kasender); +} + +PacketPassInterface * KeepaliveIO_GetInput (KeepaliveIO *o) +{ + DebugObject_Access(&o->d_obj); + + return PacketPassPriorityQueueFlow_GetInput(&o->user_qflow); +} diff --git a/external/badvpn_dns/flowextra/KeepaliveIO.h b/external/badvpn_dns/flowextra/KeepaliveIO.h new file mode 100644 index 00000000..d89321cd --- /dev/null +++ b/external/badvpn_dns/flowextra/KeepaliveIO.h @@ -0,0 +1,88 @@ +/** + * @file KeepaliveIO.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer for sending keep-alive packets. + */ + +#ifndef BADVPN_KEEPALIVEIO +#define BADVPN_KEEPALIVEIO + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * A {@link PacketPassInterface} layer for sending keep-alive packets. + */ +typedef struct { + BReactor *reactor; + PacketPassInactivityMonitor kasender; + PacketPassPriorityQueue queue; + PacketPassPriorityQueueFlow user_qflow; + PacketPassPriorityQueueFlow ka_qflow; + SinglePacketBuffer ka_buffer; + PacketRecvBlocker ka_blocker; + DebugObject d_obj; +} KeepaliveIO; + +/** + * Initializes the object. + * + * @param o the object + * @param reactor reactor we live in + * @param output output interface + * @param keepalive_input keepalive input interface. Its MTU must be <= MTU of output. + * @param keepalive_interval_ms keepalive interval in milliseconds. Must be >0. + * @return 1 on success, 0 on failure + */ +int KeepaliveIO_Init (KeepaliveIO *o, BReactor *reactor, PacketPassInterface *output, PacketRecvInterface *keepalive_input, btime_t keepalive_interval_ms) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void KeepaliveIO_Free (KeepaliveIO *o); + +/** + * Returns the input interface. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * KeepaliveIO_GetInput (KeepaliveIO *o); + +#endif diff --git a/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.c b/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.c new file mode 100644 index 00000000..6531fe01 --- /dev/null +++ b/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.c @@ -0,0 +1,131 @@ +/** + * @file PacketPassInactivityMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "PacketPassInactivityMonitor.h" + +static void input_handler_send (PacketPassInactivityMonitor *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // schedule send + PacketPassInterface_Sender_Send(o->output, data, data_len); + + // stop timer + BReactor_RemoveTimer(o->reactor, &o->timer); +} + +static void input_handler_requestcancel (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // request cancel + PacketPassInterface_Sender_RequestCancel(o->output); +} + +static void output_handler_done (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // output no longer busy, restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call done + PacketPassInterface_Done(&o->input); +} + +static void timer_handler (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + // restart timer + BReactor_SetTimer(o->reactor, &o->timer); + + // call handler + if (o->handler) { + o->handler(o->user); + return; + } +} + +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user) +{ + // init arguments + o->output = output; + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // init input + PacketPassInterface_Init(&o->input, PacketPassInterface_GetMTU(o->output), (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor)); + if (PacketPassInterface_HasCancel(o->output)) { + PacketPassInterface_EnableCancel(&o->input, (PacketPassInterface_handler_requestcancel)input_handler_requestcancel); + } + + // init output + PacketPassInterface_Sender_Init(o->output, (PacketPassInterface_handler_done)output_handler_done, o); + + // init timer + BTimer_Init(&o->timer, interval, (BTimer_handler)timer_handler, o); + BReactor_SetTimer(o->reactor, &o->timer); + + DebugObject_Init(&o->d_obj); +} + +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o) +{ + DebugObject_Free(&o->d_obj); + + // free timer + BReactor_RemoveTimer(o->reactor, &o->timer); + + // free input + PacketPassInterface_Free(&o->input); +} + +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->input; +} + +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user) +{ + DebugObject_Access(&o->d_obj); + + o->handler = handler; + o->user = user; +} + +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + BReactor_SetTimerAfter(o->reactor, &o->timer, 0); +} diff --git a/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.h b/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.h new file mode 100644 index 00000000..58576180 --- /dev/null +++ b/external/badvpn_dns/flowextra/PacketPassInactivityMonitor.h @@ -0,0 +1,124 @@ +/** + * @file PacketPassInactivityMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketPassInterface} layer for detecting inactivity. + */ + +#ifndef BADVPN_PACKETPASSINACTIVITYMONITOR_H +#define BADVPN_PACKETPASSINACTIVITYMONITOR_H + +#include +#include +#include + +/** + * Handler function invoked when inactivity is detected. + * It is guaranteed that the interfaces are in not sending state. + * + * @param user value given to {@link PacketPassInactivityMonitor_Init} + */ +typedef void (*PacketPassInactivityMonitor_handler) (void *user); + +/** + * A {@link PacketPassInterface} layer for detecting inactivity. + * It reports inactivity to a user provided handler function. + * + * The object behaves like that: + * ("timer set" means started with the given timeout whether if was running or not, + * "timer unset" means stopped if it was running) + * - There is a timer. + * - The timer is set when the object is initialized. + * - When the input calls Send, the call is passed on to the output. + * If the output accepted the packet, the timer is set. If the output + * blocked the packet, the timer is unset. + * - When the output calls Done, the timer is set, and the call is + * passed on to the input. + * - When the input calls Cancel, the timer is set, and the call is + * passed on to the output. + * - When the timer expires, the timer is set, ant the user's handler + * function is invoked. + */ +typedef struct { + DebugObject d_obj; + PacketPassInterface *output; + BReactor *reactor; + PacketPassInactivityMonitor_handler handler; + void *user; + PacketPassInterface input; + BTimer timer; +} PacketPassInactivityMonitor; + +/** + * Initializes the object. + * See {@link PacketPassInactivityMonitor} for details. + * + * @param o the object + * @param output output interface + * @param reactor reactor we live in + * @param interval timer value in milliseconds + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_Init (PacketPassInactivityMonitor *o, PacketPassInterface *output, BReactor *reactor, btime_t interval, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Frees the object. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Free (PacketPassInactivityMonitor *o); + +/** + * Returns the input interface. + * The MTU of the interface will be the same as of the output interface. + * The interface supports cancel functionality if the output interface supports it. + * + * @param o the object + * @return input interface + */ +PacketPassInterface * PacketPassInactivityMonitor_GetInput (PacketPassInactivityMonitor *o); + +/** + * Sets or removes the inactivity handler. + * + * @param o the object + * @param handler handler function for reporting inactivity, or NULL to disable + * @param user value passed to handler functions + */ +void PacketPassInactivityMonitor_SetHandler (PacketPassInactivityMonitor *o, PacketPassInactivityMonitor_handler handler, void *user); + +/** + * Sets the timer to expire immediately in order to force an inactivity report. + * + * @param o the object + */ +void PacketPassInactivityMonitor_Force (PacketPassInactivityMonitor *o); + +#endif diff --git a/external/badvpn_dns/generate_files b/external/badvpn_dns/generate_files new file mode 100755 index 00000000..f473369f --- /dev/null +++ b/external/badvpn_dns/generate_files @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e + +PHP_CMD=( php ) +FLEX_CMD=( flex ) +BISON_CMD=( bison ) + +OUT_DIR="generated/" + +function bproto() { + local input="$1" + local name="$2" + "${PHP_CMD[@]}" bproto_generator/bproto.php --input-file "${input}" --output-dir "${OUT_DIR}" --name "bproto_${name}" +} + +function do_flex() { + local input="$1" + local name="$2" + "${FLEX_CMD[@]}" -o "${OUT_DIR}/flex_${name}.c" --header-file="${OUT_DIR}/flex_${name}.h" "${input}" + "${PHP_CMD[@]}" fix_flex.php "${OUT_DIR}/flex_${name}.c" + "${PHP_CMD[@]}" fix_flex.php "${OUT_DIR}/flex_${name}.h" +} + +function do_bison() { + local input="$1" + local name="$2" + "${BISON_CMD[@]}" -d -o "${OUT_DIR}/bison_${name}.c" "${input}" +} + +function do_lemon() { + local input="$1" + local name=$(basename "${input}") + ( + cd generated && + rm -f "${name}" && + cp ../"${input}" "${name}" && + ../lemon/lemon "${name}" + ) +} + +mkdir -p generated + +bproto tests/bproto_test.bproto bproto_test +bproto protocol/msgproto.bproto msgproto +bproto protocol/addr.bproto addr +do_flex predicate/BPredicate.l BPredicate +do_bison predicate/BPredicate.y BPredicate +"${PHP_CMD[@]}" blog_generator/blog.php --input-file blog_channels.txt --output-dir "${OUT_DIR}" +do_lemon ncd/NCDConfigParser_parse.y +do_lemon ncd/NCDValParser_parse.y diff --git a/external/badvpn_dns/generated/NCDConfigParser_parse.c b/external/badvpn_dns/generated/NCDConfigParser_parse.c new file mode 100644 index 00000000..3ebdcebb --- /dev/null +++ b/external/badvpn_dns/generated/NCDConfigParser_parse.c @@ -0,0 +1,1890 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +#line 30 "NCDConfigParser_parse.y" + + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +#line 62 "NCDConfigParser_parse.c" +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +#define YYCODETYPE unsigned char +#define YYNOCODE 41 +#define YYACTIONTYPE unsigned char +#define ParseTOKENTYPE struct token +typedef union { + int yyinit; + ParseTOKENTYPE yy0; + int yy12; + char * yy33; + struct block yy37; + struct value yy51; + struct program yy54; + struct ifblock yy76; + struct statement yy79; +} YYMINORTYPE; +#ifndef YYSTACKDEPTH +#define YYSTACKDEPTH 0 +#endif +#define ParseARG_SDECL struct parser_out *parser_out ; +#define ParseARG_PDECL , struct parser_out *parser_out +#define ParseARG_FETCH struct parser_out *parser_out = yypParser->parser_out +#define ParseARG_STORE yypParser->parser_out = parser_out +#define YYNSTATE 99 +#define YYNRULE 38 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static const YYACTIONTYPE yy_action[] = { + /* 0 */ 86, 39, 80, 87, 68, 88, 42, 86, 48, 80, + /* 10 */ 87, 1, 88, 42, 24, 85, 78, 41, 3, 82, + /* 20 */ 86, 40, 43, 87, 41, 88, 42, 41, 85, 79, + /* 30 */ 41, 3, 4, 86, 50, 56, 87, 46, 88, 44, + /* 40 */ 47, 85, 49, 41, 3, 4, 89, 34, 86, 35, + /* 50 */ 81, 87, 72, 88, 42, 73, 54, 86, 4, 55, + /* 60 */ 87, 84, 88, 44, 26, 97, 36, 75, 76, 36, + /* 70 */ 86, 61, 66, 87, 86, 88, 45, 87, 86, 88, + /* 80 */ 51, 87, 86, 88, 57, 87, 33, 88, 69, 15, + /* 90 */ 59, 27, 98, 38, 31, 99, 62, 37, 18, 15, + /* 100 */ 36, 138, 15, 53, 31, 15, 67, 31, 15, 60, + /* 110 */ 31, 15, 94, 31, 15, 65, 31, 19, 71, 31, + /* 120 */ 20, 11, 77, 23, 74, 22, 5, 83, 6, 90, + /* 130 */ 2, 91, 7, 25, 8, 12, 52, 21, 36, 13, + /* 140 */ 9, 92, 58, 32, 28, 14, 93, 63, 29, 64, + /* 150 */ 16, 139, 96, 95, 10, 17, 70, 30, +}; +static const YYCODETYPE yy_lookahead[] = { + /* 0 */ 30, 31, 32, 33, 15, 35, 36, 30, 31, 32, + /* 10 */ 33, 7, 35, 36, 10, 2, 4, 4, 5, 6, + /* 20 */ 30, 37, 32, 33, 4, 35, 36, 4, 2, 30, + /* 30 */ 4, 5, 19, 30, 11, 12, 33, 34, 35, 36, + /* 40 */ 30, 2, 37, 4, 5, 19, 20, 1, 30, 3, + /* 50 */ 32, 33, 24, 35, 36, 24, 37, 30, 19, 16, + /* 60 */ 33, 34, 35, 36, 26, 27, 38, 21, 22, 38, + /* 70 */ 30, 37, 37, 33, 30, 35, 36, 33, 30, 35, + /* 80 */ 36, 33, 30, 35, 36, 33, 24, 35, 36, 25, + /* 90 */ 8, 28, 27, 29, 30, 0, 14, 4, 2, 25, + /* 100 */ 38, 39, 25, 29, 30, 25, 29, 30, 25, 29, + /* 110 */ 30, 25, 29, 30, 25, 29, 30, 2, 29, 30, + /* 120 */ 6, 5, 9, 17, 24, 8, 18, 6, 18, 20, + /* 130 */ 7, 9, 14, 8, 7, 5, 8, 6, 38, 5, + /* 140 */ 7, 9, 13, 4, 6, 5, 9, 4, 6, 8, + /* 150 */ 5, 40, 6, 9, 7, 5, 8, 6, +}; +#define YY_SHIFT_USE_DFLT (-12) +#define YY_SHIFT_MAX 71 +static const short yy_shift_ofst[] = { + /* 0 */ 46, 39, 39, 13, 26, 39, 39, 39, 39, 39, + /* 10 */ 39, 23, 23, 23, 23, 23, 23, 23, 46, 46, + /* 20 */ 46, -11, 12, 20, 20, 12, 43, 12, 12, 12, + /* 30 */ -11, 4, 82, 95, 96, 115, 93, 116, 114, 117, + /* 40 */ 113, 106, 108, 121, 118, 110, 109, 123, 125, 122, + /* 50 */ 127, 128, 130, 131, 132, 134, 133, 129, 139, 140, + /* 60 */ 138, 137, 143, 141, 145, 142, 144, 146, 147, 148, + /* 70 */ 150, 151, +}; +#define YY_REDUCE_USE_DFLT (-31) +#define YY_REDUCE_MAX 30 +static const signed char yy_reduce_ofst[] = { + /* 0 */ 62, -30, -23, -10, 3, 18, 27, 40, 44, 48, + /* 10 */ 52, 64, 74, 77, 80, 83, 86, 89, 28, 31, + /* 20 */ 100, 38, -16, -1, 10, 5, 63, 19, 34, 35, + /* 30 */ 65, +}; +static const YYACTIONTYPE yy_default[] = { + /* 0 */ 100, 119, 119, 137, 137, 137, 137, 137, 137, 137, + /* 10 */ 137, 137, 137, 137, 137, 115, 137, 137, 100, 100, + /* 20 */ 100, 109, 133, 137, 137, 133, 113, 133, 133, 133, + /* 30 */ 111, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 40 */ 137, 117, 121, 137, 137, 125, 137, 137, 137, 137, + /* 50 */ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 60 */ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + /* 70 */ 137, 137, 101, 102, 103, 135, 136, 104, 134, 118, + /* 80 */ 120, 122, 123, 124, 126, 129, 130, 131, 132, 127, + /* 90 */ 128, 105, 106, 107, 116, 108, 114, 110, 112, +}; +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { + "$", "INCLUDE", "STRING", "INCLUDE_GUARD", + "NAME", "CURLY_OPEN", "CURLY_CLOSE", "ROUND_OPEN", + "ROUND_CLOSE", "SEMICOLON", "ARROW", "IF", + "FOREACH", "AS", "COLON", "ELIF", + "ELSE", "DOT", "COMMA", "BRACKET_OPEN", + "BRACKET_CLOSE", "PROCESS", "TEMPLATE", "error", + "processes", "statement", "elif_maybe", "elif", + "else_maybe", "statements", "dotted_name", "statement_args_maybe", + "list_contents", "list", "map_contents", "map", + "value", "name_maybe", "process_or_template", "input", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { + /* 0 */ "input ::= processes", + /* 1 */ "processes ::=", + /* 2 */ "processes ::= INCLUDE STRING processes", + /* 3 */ "processes ::= INCLUDE_GUARD STRING processes", + /* 4 */ "processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes", + /* 5 */ "statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON", + /* 6 */ "statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON", + /* 7 */ "statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON", + /* 8 */ "statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON", + /* 9 */ "statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON", + /* 10 */ "elif_maybe ::=", + /* 11 */ "elif_maybe ::= elif", + /* 12 */ "elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE", + /* 13 */ "elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif", + /* 14 */ "else_maybe ::=", + /* 15 */ "else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE", + /* 16 */ "statements ::= statement", + /* 17 */ "statements ::= statement statements", + /* 18 */ "dotted_name ::= NAME", + /* 19 */ "dotted_name ::= NAME DOT dotted_name", + /* 20 */ "statement_args_maybe ::=", + /* 21 */ "statement_args_maybe ::= list_contents", + /* 22 */ "list_contents ::= value", + /* 23 */ "list_contents ::= value COMMA list_contents", + /* 24 */ "list ::= CURLY_OPEN CURLY_CLOSE", + /* 25 */ "list ::= CURLY_OPEN list_contents CURLY_CLOSE", + /* 26 */ "map_contents ::= value COLON value", + /* 27 */ "map_contents ::= value COLON value COMMA map_contents", + /* 28 */ "map ::= BRACKET_OPEN BRACKET_CLOSE", + /* 29 */ "map ::= BRACKET_OPEN map_contents BRACKET_CLOSE", + /* 30 */ "value ::= STRING", + /* 31 */ "value ::= dotted_name", + /* 32 */ "value ::= list", + /* 33 */ "value ::= map", + /* 34 */ "name_maybe ::=", + /* 35 */ "name_maybe ::= NAME", + /* 36 */ "process_or_template ::= PROCESS", + /* 37 */ "process_or_template ::= TEMPLATE", +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + /* TERMINAL Destructor */ + case 1: /* INCLUDE */ + case 2: /* STRING */ + case 3: /* INCLUDE_GUARD */ + case 4: /* NAME */ + case 5: /* CURLY_OPEN */ + case 6: /* CURLY_CLOSE */ + case 7: /* ROUND_OPEN */ + case 8: /* ROUND_CLOSE */ + case 9: /* SEMICOLON */ + case 10: /* ARROW */ + case 11: /* IF */ + case 12: /* FOREACH */ + case 13: /* AS */ + case 14: /* COLON */ + case 15: /* ELIF */ + case 16: /* ELSE */ + case 17: /* DOT */ + case 18: /* COMMA */ + case 19: /* BRACKET_OPEN */ + case 20: /* BRACKET_CLOSE */ + case 21: /* PROCESS */ + case 22: /* TEMPLATE */ +{ +#line 89 "NCDConfigParser_parse.y" + free_token((yypminor->yy0)); +#line 523 "NCDConfigParser_parse.c" +} + break; + case 24: /* processes */ +{ +#line 108 "NCDConfigParser_parse.y" + (void)parser_out; free_program((yypminor->yy54)); +#line 530 "NCDConfigParser_parse.c" +} + break; + case 25: /* statement */ +{ +#line 109 "NCDConfigParser_parse.y" + free_statement((yypminor->yy79)); +#line 537 "NCDConfigParser_parse.c" +} + break; + case 26: /* elif_maybe */ + case 27: /* elif */ +{ +#line 110 "NCDConfigParser_parse.y" + free_ifblock((yypminor->yy76)); +#line 545 "NCDConfigParser_parse.c" +} + break; + case 28: /* else_maybe */ + case 29: /* statements */ +{ +#line 112 "NCDConfigParser_parse.y" + free_block((yypminor->yy37)); +#line 553 "NCDConfigParser_parse.c" +} + break; + case 30: /* dotted_name */ + case 37: /* name_maybe */ +{ +#line 114 "NCDConfigParser_parse.y" + free((yypminor->yy33)); +#line 561 "NCDConfigParser_parse.c" +} + break; + case 31: /* statement_args_maybe */ + case 32: /* list_contents */ + case 33: /* list */ + case 34: /* map_contents */ + case 35: /* map */ + case 36: /* value */ +{ +#line 115 "NCDConfigParser_parse.y" + free_value((yypminor->yy51)); +#line 573 "NCDConfigParser_parse.c" +} + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +#line 130 "NCDConfigParser_parse.y" + + if (yypMinor) { + free_token(yypMinor->yy0); + } +#line 751 "NCDConfigParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 39, 1 }, + { 24, 0 }, + { 24, 3 }, + { 24, 3 }, + { 24, 6 }, + { 25, 6 }, + { 25, 8 }, + { 25, 11 }, + { 25, 11 }, + { 25, 13 }, + { 26, 0 }, + { 26, 1 }, + { 27, 7 }, + { 27, 8 }, + { 28, 0 }, + { 28, 4 }, + { 29, 1 }, + { 29, 2 }, + { 30, 1 }, + { 30, 3 }, + { 31, 0 }, + { 31, 1 }, + { 32, 1 }, + { 32, 3 }, + { 33, 2 }, + { 33, 3 }, + { 34, 3 }, + { 34, 5 }, + { 35, 2 }, + { 35, 3 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 37, 0 }, + { 37, 1 }, + { 38, 1 }, + { 38, 1 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ + case 0: /* input ::= processes */ +#line 136 "NCDConfigParser_parse.y" +{ + ASSERT(!parser_out->have_ast) + + if (yymsp[0].minor.yy54.have) { + parser_out->have_ast = 1; + parser_out->ast = yymsp[0].minor.yy54.v; + } +} +#line 910 "NCDConfigParser_parse.c" + break; + case 1: /* processes ::= */ +#line 145 "NCDConfigParser_parse.y" +{ + NCDProgram prog; + NCDProgram_Init(&prog); + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = prog; +} +#line 921 "NCDConfigParser_parse.c" + break; + case 2: /* processes ::= INCLUDE STRING processes */ +#line 153 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-1].minor.yy0.str) + if (!yymsp[0].minor.yy54.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, yymsp[-1].minor.yy0.str, yymsp[-1].minor.yy0.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failA1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(yymsp[-1].minor.yy0); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,1,&yymsp[-2].minor); +} +#line 955 "NCDConfigParser_parse.c" + break; + case 3: /* processes ::= INCLUDE_GUARD STRING processes */ +#line 183 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-1].minor.yy0.str) + if (!yymsp[0].minor.yy54.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, yymsp[-1].minor.yy0.str, yymsp[-1].minor.yy0.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failZ1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(yymsp[-1].minor.yy0); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,3,&yymsp[-2].minor); +} +#line 989 "NCDConfigParser_parse.c" + break; + case 4: /* processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes */ +#line 213 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-4].minor.yy0.str) + if (!yymsp[-2].minor.yy37.have || !yymsp[0].minor.yy54.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, yymsp[-5].minor.yy12, yymsp[-4].minor.yy0.str, yymsp[-2].minor.yy37.v)) { + goto failB0; + } + yymsp[-2].minor.yy37.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&yymsp[0].minor.yy54.v, elem)) { + goto failB1; + } + + yygotominor.yy54.have = 1; + yygotominor.yy54.v = yymsp[0].minor.yy54.v; + yymsp[0].minor.yy54.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + yygotominor.yy54.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(yymsp[-4].minor.yy0); + free_block(yymsp[-2].minor.yy37); + free_program(yymsp[0].minor.yy54); + yy_destructor(yypParser,5,&yymsp[-3].minor); + yy_destructor(yypParser,6,&yymsp[-1].minor); +} +#line 1029 "NCDConfigParser_parse.c" + break; + case 5: /* statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */ +#line 248 "NCDConfigParser_parse.y" +{ + if (!yymsp[-5].minor.yy33 || !yymsp[-3].minor.yy51.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&yygotominor.yy79.v, yymsp[-1].minor.yy33, NULL, yymsp[-5].minor.yy33, yymsp[-3].minor.yy51.v)) { + goto failC0; + } + yymsp[-3].minor.yy51.have = 0; + + yygotominor.yy79.have = 1; + goto doneC; + +failC0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(yymsp[-5].minor.yy33); + free_value(yymsp[-3].minor.yy51); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,7,&yymsp[-4].minor); + yy_destructor(yypParser,8,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1057 "NCDConfigParser_parse.c" + break; + case 6: /* statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON */ +#line 270 "NCDConfigParser_parse.y" +{ + if (!yymsp[-7].minor.yy33 || !yymsp[-5].minor.yy33 || !yymsp[-3].minor.yy51.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-7].minor.yy33, yymsp[-5].minor.yy33, yymsp[-3].minor.yy51.v)) { + goto failD0; + } + yymsp[-3].minor.yy51.have = 0; + + yygotominor.yy79.have = 1; + goto doneD; + +failD0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(yymsp[-7].minor.yy33); + free(yymsp[-5].minor.yy33); + free_value(yymsp[-3].minor.yy51); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,10,&yymsp[-6].minor); + yy_destructor(yypParser,7,&yymsp[-4].minor); + yy_destructor(yypParser,8,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1087 "NCDConfigParser_parse.c" + break; + case 7: /* statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON */ +#line 293 "NCDConfigParser_parse.y" +{ + if (!yymsp[-8].minor.yy51.have || !yymsp[-5].minor.yy37.have || !yymsp[-3].minor.yy76.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-8].minor.yy51.v, yymsp[-5].minor.yy37.v); + yymsp[-8].minor.yy51.have = 0; + yymsp[-5].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yymsp[-3].minor.yy76.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-3].minor.yy76.v)) { + goto failE0; + } + yymsp[-3].minor.yy76.have = 0; + + if (yymsp[-2].minor.yy37.have) { + NCDStatement_IfAddElse(&yygotominor.yy79.v, yymsp[-2].minor.yy37.v); + yymsp[-2].minor.yy37.have = 0; + } + + yygotominor.yy79.have = 1; + goto doneE; + +failE0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(yymsp[-8].minor.yy51); + free_block(yymsp[-5].minor.yy37); + free_ifblock(yymsp[-3].minor.yy76); + free_block(yymsp[-2].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,11,&yymsp[-10].minor); + yy_destructor(yypParser,7,&yymsp[-9].minor); + yy_destructor(yypParser,8,&yymsp[-7].minor); + yy_destructor(yypParser,5,&yymsp[-6].minor); + yy_destructor(yypParser,6,&yymsp[-4].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1135 "NCDConfigParser_parse.c" + break; + case 8: /* statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON */ +#line 332 "NCDConfigParser_parse.y" +{ + if (!yymsp[-8].minor.yy51.have || !yymsp[-6].minor.yy0.str || !yymsp[-3].minor.yy37.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-8].minor.yy51.v, yymsp[-6].minor.yy0.str, NULL, yymsp[-3].minor.yy37.v)) { + goto failEA0; + } + yymsp[-8].minor.yy51.have = 0; + yymsp[-3].minor.yy37.have = 0; + + yygotominor.yy79.have = 1; + goto doneEA0; + +failEA0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(yymsp[-8].minor.yy51); + free_token(yymsp[-6].minor.yy0); + free_block(yymsp[-3].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,12,&yymsp[-10].minor); + yy_destructor(yypParser,7,&yymsp[-9].minor); + yy_destructor(yypParser,13,&yymsp[-7].minor); + yy_destructor(yypParser,8,&yymsp[-5].minor); + yy_destructor(yypParser,5,&yymsp[-4].minor); + yy_destructor(yypParser,6,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1169 "NCDConfigParser_parse.c" + break; + case 9: /* statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON */ +#line 356 "NCDConfigParser_parse.y" +{ + if (!yymsp[-10].minor.yy51.have || !yymsp[-8].minor.yy0.str || !yymsp[-6].minor.yy0.str || !yymsp[-3].minor.yy37.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&yygotominor.yy79.v, yymsp[-1].minor.yy33, yymsp[-10].minor.yy51.v, yymsp[-8].minor.yy0.str, yymsp[-6].minor.yy0.str, yymsp[-3].minor.yy37.v)) { + goto failEB0; + } + yymsp[-10].minor.yy51.have = 0; + yymsp[-3].minor.yy37.have = 0; + + yygotominor.yy79.have = 1; + goto doneEB0; + +failEB0: + yygotominor.yy79.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(yymsp[-10].minor.yy51); + free_token(yymsp[-8].minor.yy0); + free_token(yymsp[-6].minor.yy0); + free_block(yymsp[-3].minor.yy37); + free(yymsp[-1].minor.yy33); + yy_destructor(yypParser,12,&yymsp[-12].minor); + yy_destructor(yypParser,7,&yymsp[-11].minor); + yy_destructor(yypParser,13,&yymsp[-9].minor); + yy_destructor(yypParser,14,&yymsp[-7].minor); + yy_destructor(yypParser,8,&yymsp[-5].minor); + yy_destructor(yypParser,5,&yymsp[-4].minor); + yy_destructor(yypParser,6,&yymsp[-2].minor); + yy_destructor(yypParser,9,&yymsp[0].minor); +} +#line 1205 "NCDConfigParser_parse.c" + break; + case 10: /* elif_maybe ::= */ +#line 381 "NCDConfigParser_parse.y" +{ + NCDIfBlock_Init(&yygotominor.yy76.v); + yygotominor.yy76.have = 1; +} +#line 1213 "NCDConfigParser_parse.c" + break; + case 11: /* elif_maybe ::= elif */ +#line 386 "NCDConfigParser_parse.y" +{ + yygotominor.yy76 = yymsp[0].minor.yy76; +} +#line 1220 "NCDConfigParser_parse.c" + break; + case 12: /* elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE */ +#line 390 "NCDConfigParser_parse.y" +{ + if (!yymsp[-4].minor.yy51.have || !yymsp[-1].minor.yy37.have) { + goto failF0; + } + + NCDIfBlock_Init(&yygotominor.yy76.v); + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-4].minor.yy51.v, yymsp[-1].minor.yy37.v); + yymsp[-4].minor.yy51.have = 0; + yymsp[-1].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yygotominor.yy76.v, ifc)) { + goto failF1; + } + + yygotominor.yy76.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&yygotominor.yy76.v); +failF0: + yygotominor.yy76.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(yymsp[-4].minor.yy51); + free_block(yymsp[-1].minor.yy37); + yy_destructor(yypParser,15,&yymsp[-6].minor); + yy_destructor(yypParser,7,&yymsp[-5].minor); + yy_destructor(yypParser,8,&yymsp[-3].minor); + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1258 "NCDConfigParser_parse.c" + break; + case 13: /* elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif */ +#line 420 "NCDConfigParser_parse.y" +{ + if (!yymsp[-5].minor.yy51.have || !yymsp[-2].minor.yy37.have || !yymsp[0].minor.yy76.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, yymsp[-5].minor.yy51.v, yymsp[-2].minor.yy37.v); + yymsp[-5].minor.yy51.have = 0; + yymsp[-2].minor.yy37.have = 0; + + if (!NCDIfBlock_PrependIf(&yymsp[0].minor.yy76.v, ifc)) { + goto failG1; + } + + yygotominor.yy76.have = 1; + yygotominor.yy76.v = yymsp[0].minor.yy76.v; + yymsp[0].minor.yy76.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + yygotominor.yy76.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(yymsp[-5].minor.yy51); + free_block(yymsp[-2].minor.yy37); + free_ifblock(yymsp[0].minor.yy76); + yy_destructor(yypParser,15,&yymsp[-7].minor); + yy_destructor(yypParser,7,&yymsp[-6].minor); + yy_destructor(yypParser,8,&yymsp[-4].minor); + yy_destructor(yypParser,5,&yymsp[-3].minor); + yy_destructor(yypParser,6,&yymsp[-1].minor); +} +#line 1296 "NCDConfigParser_parse.c" + break; + case 14: /* else_maybe ::= */ +#line 450 "NCDConfigParser_parse.y" +{ + yygotominor.yy37.have = 0; +} +#line 1303 "NCDConfigParser_parse.c" + break; + case 15: /* else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE */ +#line 454 "NCDConfigParser_parse.y" +{ + yygotominor.yy37 = yymsp[-1].minor.yy37; + yy_destructor(yypParser,16,&yymsp[-3].minor); + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1313 "NCDConfigParser_parse.c" + break; + case 16: /* statements ::= statement */ +#line 458 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy79.have) { + goto failH0; + } + + NCDBlock_Init(&yygotominor.yy37.v); + + if (!NCDBlock_PrependStatement(&yygotominor.yy37.v, yymsp[0].minor.yy79.v)) { + goto failH1; + } + yymsp[0].minor.yy79.have = 0; + + yygotominor.yy37.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&yygotominor.yy37.v); +failH0: + yygotominor.yy37.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(yymsp[0].minor.yy79); +} +#line 1340 "NCDConfigParser_parse.c" + break; + case 17: /* statements ::= statement statements */ +#line 482 "NCDConfigParser_parse.y" +{ + if (!yymsp[-1].minor.yy79.have || !yymsp[0].minor.yy37.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&yymsp[0].minor.yy37.v, yymsp[-1].minor.yy79.v)) { + goto failI1; + } + yymsp[-1].minor.yy79.have = 0; + + yygotominor.yy37.have = 1; + yygotominor.yy37.v = yymsp[0].minor.yy37.v; + yymsp[0].minor.yy37.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&yygotominor.yy37.v); +failI0: + yygotominor.yy37.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(yymsp[-1].minor.yy79); + free_block(yymsp[0].minor.yy37); +} +#line 1368 "NCDConfigParser_parse.c" + break; + case 18: /* dotted_name ::= NAME */ + case 35: /* name_maybe ::= NAME */ yytestcase(yyruleno==35); +#line 507 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + yygotominor.yy33 = yymsp[0].minor.yy0.str; +} +#line 1378 "NCDConfigParser_parse.c" + break; + case 19: /* dotted_name ::= NAME DOT dotted_name */ +#line 513 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[-2].minor.yy0.str) + if (!yymsp[0].minor.yy33) { + goto failJ0; + } + + if (!(yygotominor.yy33 = concat_strings(3, yymsp[-2].minor.yy0.str, ".", yymsp[0].minor.yy33))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + yygotominor.yy33 = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(yymsp[-2].minor.yy0); + free(yymsp[0].minor.yy33); + yy_destructor(yypParser,17,&yymsp[-1].minor); +} +#line 1402 "NCDConfigParser_parse.c" + break; + case 20: /* statement_args_maybe ::= */ +#line 533 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitList(&yygotominor.yy51.v); +} +#line 1410 "NCDConfigParser_parse.c" + break; + case 21: /* statement_args_maybe ::= list_contents */ + case 32: /* value ::= list */ yytestcase(yyruleno==32); + case 33: /* value ::= map */ yytestcase(yyruleno==33); +#line 538 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[0].minor.yy51; +} +#line 1419 "NCDConfigParser_parse.c" + break; + case 22: /* list_contents ::= value */ +#line 542 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy51.have) { + goto failL0; + } + + NCDValue_InitList(&yygotominor.yy51.v); + + if (!NCDValue_ListPrepend(&yygotominor.yy51.v, yymsp[0].minor.yy51.v)) { + goto failL1; + } + yymsp[0].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&yygotominor.yy51.v); +failL0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(yymsp[0].minor.yy51); +} +#line 1446 "NCDConfigParser_parse.c" + break; + case 23: /* list_contents ::= value COMMA list_contents */ +#line 566 "NCDConfigParser_parse.y" +{ + if (!yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&yymsp[0].minor.yy51.v, yymsp[-2].minor.yy51.v)) { + goto failM0; + } + yymsp[-2].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + yygotominor.yy51.v = yymsp[0].minor.yy51.v; + yymsp[0].minor.yy51.have = 0; + goto doneM; + +failM0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,18,&yymsp[-1].minor); +} +#line 1473 "NCDConfigParser_parse.c" + break; + case 24: /* list ::= CURLY_OPEN CURLY_CLOSE */ +#line 589 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitList(&yygotominor.yy51.v); + yy_destructor(yypParser,5,&yymsp[-1].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1483 "NCDConfigParser_parse.c" + break; + case 25: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */ +#line 594 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[-1].minor.yy51; + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 1492 "NCDConfigParser_parse.c" + break; + case 26: /* map_contents ::= value COLON value */ +#line 598 "NCDConfigParser_parse.y" +{ + if (!yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failS0; + } + + NCDValue_InitMap(&yygotominor.yy51.v); + + if (!NCDValue_MapPrepend(&yygotominor.yy51.v, yymsp[-2].minor.yy51.v, yymsp[0].minor.yy51.v)) { + goto failS1; + } + yymsp[-2].minor.yy51.have = 0; + yymsp[0].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&yygotominor.yy51.v); +failS0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,14,&yymsp[-1].minor); +} +#line 1522 "NCDConfigParser_parse.c" + break; + case 27: /* map_contents ::= value COLON value COMMA map_contents */ +#line 624 "NCDConfigParser_parse.y" +{ + if (!yymsp[-4].minor.yy51.have || !yymsp[-2].minor.yy51.have || !yymsp[0].minor.yy51.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&yymsp[0].minor.yy51.v, yymsp[-4].minor.yy51.v, yymsp[-2].minor.yy51.v)) { + goto failT0; + } + yymsp[-4].minor.yy51.have = 0; + yymsp[-2].minor.yy51.have = 0; + + yygotominor.yy51.have = 1; + yygotominor.yy51.v = yymsp[0].minor.yy51.v; + yymsp[0].minor.yy51.have = 0; + goto doneT; + +failT0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(yymsp[-4].minor.yy51); + free_value(yymsp[-2].minor.yy51); + free_value(yymsp[0].minor.yy51); + yy_destructor(yypParser,14,&yymsp[-3].minor); + yy_destructor(yypParser,18,&yymsp[-1].minor); +} +#line 1552 "NCDConfigParser_parse.c" + break; + case 28: /* map ::= BRACKET_OPEN BRACKET_CLOSE */ +#line 649 "NCDConfigParser_parse.y" +{ + yygotominor.yy51.have = 1; + NCDValue_InitMap(&yygotominor.yy51.v); + yy_destructor(yypParser,19,&yymsp[-1].minor); + yy_destructor(yypParser,20,&yymsp[0].minor); +} +#line 1562 "NCDConfigParser_parse.c" + break; + case 29: /* map ::= BRACKET_OPEN map_contents BRACKET_CLOSE */ +#line 654 "NCDConfigParser_parse.y" +{ + yygotominor.yy51 = yymsp[-1].minor.yy51; + yy_destructor(yypParser,19,&yymsp[-2].minor); + yy_destructor(yypParser,20,&yymsp[0].minor); +} +#line 1571 "NCDConfigParser_parse.c" + break; + case 30: /* value ::= STRING */ +#line 658 "NCDConfigParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + if (!NCDValue_InitStringBin(&yygotominor.yy51.v, (uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len)) { + goto failU0; + } + + yygotominor.yy51.have = 1; + goto doneU; + +failU0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(yymsp[0].minor.yy0); +} +#line 1591 "NCDConfigParser_parse.c" + break; + case 31: /* value ::= dotted_name */ +#line 675 "NCDConfigParser_parse.y" +{ + if (!yymsp[0].minor.yy33) { + goto failV0; + } + + if (!NCDValue_InitVar(&yygotominor.yy51.v, yymsp[0].minor.yy33)) { + goto failV0; + } + + yygotominor.yy51.have = 1; + goto doneV; + +failV0: + yygotominor.yy51.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(yymsp[0].minor.yy33); +} +#line 1613 "NCDConfigParser_parse.c" + break; + case 34: /* name_maybe ::= */ +#line 702 "NCDConfigParser_parse.y" +{ + yygotominor.yy33 = NULL; +} +#line 1620 "NCDConfigParser_parse.c" + break; + case 36: /* process_or_template ::= PROCESS */ +#line 712 "NCDConfigParser_parse.y" +{ + yygotominor.yy12 = 0; + yy_destructor(yypParser,21,&yymsp[0].minor); +} +#line 1628 "NCDConfigParser_parse.c" + break; + case 37: /* process_or_template ::= TEMPLATE */ +#line 716 "NCDConfigParser_parse.y" +{ + yygotominor.yy12 = 1; + yy_destructor(yypParser,22,&yymsp[0].minor); +} +#line 1636 "NCDConfigParser_parse.c" + break; + default: + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +#line 125 "NCDConfigParser_parse.y" + + parser_out->syntax_error = 1; +#line 1701 "NCDConfigParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/external/badvpn_dns/generated/NCDConfigParser_parse.h b/external/badvpn_dns/generated/NCDConfigParser_parse.h new file mode 100644 index 00000000..086a5740 --- /dev/null +++ b/external/badvpn_dns/generated/NCDConfigParser_parse.h @@ -0,0 +1,22 @@ +#define INCLUDE 1 +#define STRING 2 +#define INCLUDE_GUARD 3 +#define NAME 4 +#define CURLY_OPEN 5 +#define CURLY_CLOSE 6 +#define ROUND_OPEN 7 +#define ROUND_CLOSE 8 +#define SEMICOLON 9 +#define ARROW 10 +#define IF 11 +#define FOREACH 12 +#define AS 13 +#define COLON 14 +#define ELIF 15 +#define ELSE 16 +#define DOT 17 +#define COMMA 18 +#define BRACKET_OPEN 19 +#define BRACKET_CLOSE 20 +#define PROCESS 21 +#define TEMPLATE 22 diff --git a/external/badvpn_dns/generated/NCDConfigParser_parse.out b/external/badvpn_dns/generated/NCDConfigParser_parse.out new file mode 100644 index 00000000..bdf830b5 --- /dev/null +++ b/external/badvpn_dns/generated/NCDConfigParser_parse.out @@ -0,0 +1,950 @@ +State 0: + input ::= * processes + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 33 + process_or_template shift 36 + input accept + {default} reduce 1 + +State 1: + statement ::= dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + (20) statement_args_maybe ::= * + statement_args_maybe ::= * list_contents + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + statement_args_maybe shift 39 + list_contents shift 80 + list shift 87 + map shift 88 + value shift 42 + {default} reduce 20 + +State 2: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN * statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + (20) statement_args_maybe ::= * + statement_args_maybe ::= * list_contents + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + statement_args_maybe shift 48 + list_contents shift 80 + list shift 87 + map shift 88 + value shift 42 + {default} reduce 20 + +State 3: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= CURLY_OPEN * CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + list ::= CURLY_OPEN * list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + CURLY_CLOSE shift 82 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list_contents shift 43 + list shift 87 + map shift 88 + value shift 42 + +State 4: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= BRACKET_OPEN * BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + BRACKET_CLOSE shift 89 + dotted_name shift 86 + list shift 87 + map_contents shift 46 + map shift 88 + value shift 44 + +State 5: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list_contents ::= value COMMA * list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list_contents shift 81 + list shift 87 + map shift 88 + value shift 42 + +State 6: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map_contents ::= value COLON value COMMA * map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map_contents shift 84 + map shift 88 + value shift 44 + +State 7: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= value COLON * value + map_contents ::= value COLON * value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 45 + +State 8: + statement ::= IF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 51 + +State 9: + statement ::= FOREACH ROUND_OPEN * value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN * value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 57 + +State 10: + elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN * value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * dotted_name + value ::= * list + value ::= * map + + STRING shift 85 + NAME shift 41 + CURLY_OPEN shift 3 + BRACKET_OPEN shift 4 + dotted_name shift 86 + list shift 87 + map shift 88 + value shift 69 + +State 11: + processes ::= process_or_template NAME CURLY_OPEN * statements CURLY_CLOSE processes + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 38 + dotted_name shift 31 + +State 12: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 53 + dotted_name shift 31 + +State 13: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + else_maybe ::= ELSE CURLY_OPEN * statements CURLY_CLOSE + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 67 + dotted_name shift 31 + +State 14: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 60 + dotted_name shift 31 + +State 15: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + (16) statements ::= statement * + statements ::= * statement statements + statements ::= statement * statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 94 + dotted_name shift 31 + {default} reduce 16 + +State 16: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE name_maybe SEMICOLON + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 65 + dotted_name shift 31 + +State 17: + statement ::= * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= * IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= * FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN * statements CURLY_CLOSE elif + statements ::= * statement + statements ::= * statement statements + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + IF shift 50 + FOREACH shift 56 + statement shift 15 + statements shift 71 + dotted_name shift 31 + +State 18: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= INCLUDE STRING * processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 72 + process_or_template shift 36 + {default} reduce 1 + +State 19: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= INCLUDE_GUARD STRING * processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 73 + process_or_template shift 36 + {default} reduce 1 + +State 20: + (1) processes ::= * + processes ::= * INCLUDE STRING processes + processes ::= * INCLUDE_GUARD STRING processes + processes ::= * process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes + processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE * processes + process_or_template ::= * PROCESS + process_or_template ::= * TEMPLATE + + INCLUDE shift 34 + INCLUDE_GUARD shift 35 + PROCESS shift 75 + TEMPLATE shift 76 + processes shift 74 + process_or_template shift 36 + {default} reduce 1 + +State 21: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif_maybe else_maybe name_maybe SEMICOLON + (10) elif_maybe ::= * + elif_maybe ::= * elif + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ELIF shift 68 + elif_maybe shift 26 + elif shift 97 + {default} reduce 10 + +State 22: + statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 40 + {default} reduce 34 + +State 23: + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + dotted_name ::= NAME DOT * dotted_name + + NAME shift 41 + dotted_name shift 79 + +State 24: + statement ::= dotted_name ARROW * dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + dotted_name ::= * NAME + dotted_name ::= * NAME DOT dotted_name + + NAME shift 41 + dotted_name shift 47 + +State 25: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 49 + {default} reduce 34 + +State 26: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe * else_maybe name_maybe SEMICOLON + (14) else_maybe ::= * + else_maybe ::= * ELSE CURLY_OPEN statements CURLY_CLOSE + + ELSE shift 55 + else_maybe shift 27 + {default} reduce 14 + +State 27: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 54 + {default} reduce 34 + +State 28: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 61 + {default} reduce 34 + +State 29: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * name_maybe SEMICOLON + (34) name_maybe ::= * + name_maybe ::= * NAME + + NAME shift 78 + name_maybe shift 66 + {default} reduce 34 + +State 30: + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + (12) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * + elif ::= * ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE * elif + + ELIF shift 68 + elif shift 98 + {default} reduce 12 + +State 31: + statement ::= dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + statement ::= dotted_name * ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 1 + ARROW shift 24 + +State 32: + statement ::= FOREACH ROUND_OPEN value AS NAME * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS NAME * COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 59 + COLON shift 62 + +State 33: + (0) input ::= processes * + + $ reduce 0 + +State 34: + processes ::= INCLUDE * STRING processes + + STRING shift 18 + +State 35: + processes ::= INCLUDE_GUARD * STRING processes + + STRING shift 19 + +State 36: + processes ::= process_or_template * NAME CURLY_OPEN statements CURLY_CLOSE processes + + NAME shift 37 + +State 37: + processes ::= process_or_template NAME * CURLY_OPEN statements CURLY_CLOSE processes + + CURLY_OPEN shift 11 + +State 38: + processes ::= process_or_template NAME CURLY_OPEN statements * CURLY_CLOSE processes + + CURLY_CLOSE shift 20 + +State 39: + statement ::= dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 22 + +State 40: + statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 77 + +State 41: + (18) dotted_name ::= NAME * + dotted_name ::= NAME * DOT dotted_name + + DOT shift 23 + {default} reduce 18 + +State 42: + (22) list_contents ::= value * + list_contents ::= value * COMMA list_contents + + COMMA shift 5 + {default} reduce 22 + +State 43: + list ::= CURLY_OPEN list_contents * CURLY_CLOSE + + CURLY_CLOSE shift 83 + +State 44: + map_contents ::= value * COLON value + map_contents ::= value * COLON value COMMA map_contents + + COLON shift 7 + +State 45: + (26) map_contents ::= value COLON value * + map_contents ::= value COLON value * COMMA map_contents + + COMMA shift 6 + {default} reduce 26 + +State 46: + map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE + + BRACKET_CLOSE shift 90 + +State 47: + statement ::= dotted_name ARROW dotted_name * ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 2 + +State 48: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe * ROUND_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 25 + +State 49: + statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 91 + +State 50: + statement ::= IF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + ROUND_OPEN shift 8 + +State 51: + statement ::= IF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + ROUND_CLOSE shift 52 + +State 52: + statement ::= IF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + CURLY_OPEN shift 12 + +State 53: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON + + CURLY_CLOSE shift 21 + +State 54: + statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe * SEMICOLON + + SEMICOLON shift 92 + +State 55: + else_maybe ::= ELSE * CURLY_OPEN statements CURLY_CLOSE + + CURLY_OPEN shift 13 + +State 56: + statement ::= FOREACH * ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH * ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_OPEN shift 9 + +State 57: + statement ::= FOREACH ROUND_OPEN value * AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value * AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + AS shift 58 + +State 58: + statement ::= FOREACH ROUND_OPEN value AS * NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + statement ::= FOREACH ROUND_OPEN value AS * NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + NAME shift 32 + +State 59: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + CURLY_OPEN shift 14 + +State 60: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE name_maybe SEMICOLON + + CURLY_CLOSE shift 28 + +State 61: + statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 93 + +State 62: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON * NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + NAME shift 63 + +State 63: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + ROUND_CLOSE shift 64 + +State 64: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON + + CURLY_OPEN shift 16 + +State 65: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE name_maybe SEMICOLON + + CURLY_CLOSE shift 29 + +State 66: + statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe * SEMICOLON + + SEMICOLON shift 95 + +State 67: + else_maybe ::= ELSE CURLY_OPEN statements * CURLY_CLOSE + + CURLY_CLOSE shift 96 + +State 68: + elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF * ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ROUND_OPEN shift 10 + +State 69: + elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value * ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif + + ROUND_CLOSE shift 70 + +State 70: + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE * CURLY_OPEN statements CURLY_CLOSE elif + + CURLY_OPEN shift 17 + +State 71: + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE + elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements * CURLY_CLOSE elif + + CURLY_CLOSE shift 30 + +State 72: + (2) processes ::= INCLUDE STRING processes * + + {default} reduce 2 + +State 73: + (3) processes ::= INCLUDE_GUARD STRING processes * + + {default} reduce 3 + +State 74: + (4) processes ::= process_or_template NAME CURLY_OPEN statements CURLY_CLOSE processes * + + {default} reduce 4 + +State 75: + (36) process_or_template ::= PROCESS * + + {default} reduce 36 + +State 76: + (37) process_or_template ::= TEMPLATE * + + {default} reduce 37 + +State 77: + (5) statement ::= dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON * + + {default} reduce 5 + +State 78: + (35) name_maybe ::= NAME * + + {default} reduce 35 + +State 79: + (19) dotted_name ::= NAME DOT dotted_name * + + {default} reduce 19 + +State 80: + (21) statement_args_maybe ::= list_contents * + + {default} reduce 21 + +State 81: + (23) list_contents ::= value COMMA list_contents * + + {default} reduce 23 + +State 82: + (24) list ::= CURLY_OPEN CURLY_CLOSE * + + {default} reduce 24 + +State 83: + (25) list ::= CURLY_OPEN list_contents CURLY_CLOSE * + + {default} reduce 25 + +State 84: + (27) map_contents ::= value COLON value COMMA map_contents * + + {default} reduce 27 + +State 85: + (30) value ::= STRING * + + {default} reduce 30 + +State 86: + (31) value ::= dotted_name * + + {default} reduce 31 + +State 87: + (32) value ::= list * + + {default} reduce 32 + +State 88: + (33) value ::= map * + + {default} reduce 33 + +State 89: + (28) map ::= BRACKET_OPEN BRACKET_CLOSE * + + {default} reduce 28 + +State 90: + (29) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE * + + {default} reduce 29 + +State 91: + (6) statement ::= dotted_name ARROW dotted_name ROUND_OPEN statement_args_maybe ROUND_CLOSE name_maybe SEMICOLON * + + {default} reduce 6 + +State 92: + (7) statement ::= IF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif_maybe else_maybe name_maybe SEMICOLON * + + {default} reduce 7 + +State 93: + (8) statement ::= FOREACH ROUND_OPEN value AS NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON * + + {default} reduce 8 + +State 94: + (17) statements ::= statement statements * + + {default} reduce 17 + +State 95: + (9) statement ::= FOREACH ROUND_OPEN value AS NAME COLON NAME ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE name_maybe SEMICOLON * + + {default} reduce 9 + +State 96: + (15) else_maybe ::= ELSE CURLY_OPEN statements CURLY_CLOSE * + + {default} reduce 15 + +State 97: + (11) elif_maybe ::= elif * + + {default} reduce 11 + +State 98: + (13) elif ::= ELIF ROUND_OPEN value ROUND_CLOSE CURLY_OPEN statements CURLY_CLOSE elif * + + {default} reduce 13 + +---------------------------------------------------- +Symbols: + 0: $: + 1: INCLUDE + 2: STRING + 3: INCLUDE_GUARD + 4: NAME + 5: CURLY_OPEN + 6: CURLY_CLOSE + 7: ROUND_OPEN + 8: ROUND_CLOSE + 9: SEMICOLON + 10: ARROW + 11: IF + 12: FOREACH + 13: AS + 14: COLON + 15: ELIF + 16: ELSE + 17: DOT + 18: COMMA + 19: BRACKET_OPEN + 20: BRACKET_CLOSE + 21: PROCESS + 22: TEMPLATE + 23: error: + 24: processes: INCLUDE INCLUDE_GUARD PROCESS TEMPLATE + 25: statement: NAME IF FOREACH + 26: elif_maybe: ELIF + 27: elif: ELIF + 28: else_maybe: ELSE + 29: statements: NAME IF FOREACH + 30: dotted_name: NAME + 31: statement_args_maybe: STRING NAME CURLY_OPEN BRACKET_OPEN + 32: list_contents: STRING NAME CURLY_OPEN BRACKET_OPEN + 33: list: CURLY_OPEN + 34: map_contents: STRING NAME CURLY_OPEN BRACKET_OPEN + 35: map: BRACKET_OPEN + 36: value: STRING NAME CURLY_OPEN BRACKET_OPEN + 37: name_maybe: NAME + 38: process_or_template: PROCESS TEMPLATE + 39: input: INCLUDE INCLUDE_GUARD PROCESS TEMPLATE diff --git a/external/badvpn_dns/generated/NCDConfigParser_parse.y b/external/badvpn_dns/generated/NCDConfigParser_parse.y new file mode 100644 index 00000000..fdf89f6d --- /dev/null +++ b/external/badvpn_dns/generated/NCDConfigParser_parse.y @@ -0,0 +1,718 @@ +/** + * @file NCDConfigParser.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%include { + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +} + +%extra_argument { struct parser_out *parser_out } + +%token_type { struct token } + +%token_destructor { free_token($$); } + +%type processes { struct program } +%type statement { struct statement } +%type elif_maybe { struct ifblock } +%type elif { struct ifblock } +%type else_maybe { struct block } +%type statements { struct block } +%type dotted_name { char * } +%type statement_args_maybe { struct value } +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } +%type name_maybe { char * } +%type process_or_template { int } + +// mention parser_out in some destructor to a void unused variable warning +%destructor processes { (void)parser_out; free_program($$); } +%destructor statement { free_statement($$); } +%destructor elif_maybe { free_ifblock($$); } +%destructor elif { free_ifblock($$); } +%destructor else_maybe { free_block($$); } +%destructor statements { free_block($$); } +%destructor dotted_name { free($$); } +%destructor statement_args_maybe { free_value($$); } +%destructor list_contents { free_value($$); } +%destructor list { free_value($$); } +%destructor map_contents { free_value($$); } +%destructor map { free_value($$); } +%destructor value { free_value($$); } +%destructor name_maybe { free($$); } + +%stack_size 0 + +%syntax_error { + parser_out->syntax_error = 1; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= processes(A). { + ASSERT(!parser_out->have_ast) + + if (A.have) { + parser_out->have_ast = 1; + parser_out->ast = A.v; + } +} + +processes(R) ::= . { + NCDProgram prog; + NCDProgram_Init(&prog); + + R.have = 1; + R.v = prog; +} + +processes(R) ::= INCLUDE STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, A.str, A.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failA1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(A); + free_program(N); +} + +processes(R) ::= INCLUDE_GUARD STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, A.str, A.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failZ1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + R.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(A); + free_program(N); +} + +processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). { + ASSERT(A.str) + if (!B.have || !N.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, T, A.str, B.v)) { + goto failB0; + } + B.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failB1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(A); + free_block(B); + free_program(N); +} + +statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!A || !B.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) { + goto failC0; + } + B.have = 0; + + R.have = 1; + goto doneC; + +failC0: + R.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(A); + free_value(B); + free(C); +} + +statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!M || !A || !B.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) { + goto failD0; + } + B.have = 0; + + R.have = 1; + goto doneD; + +failD0: + R.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(M); + free(A); + free_value(B); + free(C); +} + +statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. { + if (!A.have || !B.have || !I.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&I.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&R.v, C, I.v)) { + goto failE0; + } + I.have = 0; + + if (E.have) { + NCDStatement_IfAddElse(&R.v, E.v); + E.have = 0; + } + + R.have = 1; + goto doneE; + +failE0: + R.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(A); + free_block(B); + free_ifblock(I); + free_block(E); + free(C); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !S.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, NULL, S.v)) { + goto failEA0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEA0; + +failEA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(A); + free_token(B); + free_block(S); + free(N); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) COLON NAME(C) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !C.str || !S.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, C.str, S.v)) { + goto failEB0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEB0; + +failEB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(A); + free_token(B); + free_token(C); + free_block(S); + free(N); +} + +elif_maybe(R) ::= . { + NCDIfBlock_Init(&R.v); + R.have = 1; +} + +elif_maybe(R) ::= elif(A). { + R = A; +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. { + if (!A.have || !B.have) { + goto failF0; + } + + NCDIfBlock_Init(&R.v); + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&R.v, ifc)) { + goto failF1; + } + + R.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&R.v); +failF0: + R.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(A); + free_block(B); +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). { + if (!A.have || !B.have || !N.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&N.v, ifc)) { + goto failG1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + R.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(A); + free_block(B); + free_ifblock(N); +} + +else_maybe(R) ::= . { + R.have = 0; +} + +else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. { + R = B; +} + +statements(R) ::= statement(A). { + if (!A.have) { + goto failH0; + } + + NCDBlock_Init(&R.v); + + if (!NCDBlock_PrependStatement(&R.v, A.v)) { + goto failH1; + } + A.have = 0; + + R.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&R.v); +failH0: + R.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(A); +} + +statements(R) ::= statement(A) statements(N). { + if (!A.have || !N.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&N.v, A.v)) { + goto failI1; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&R.v); +failI0: + R.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(A); + free_block(N); +} + +dotted_name(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +dotted_name(R) ::= NAME(A) DOT dotted_name(N). { + ASSERT(A.str) + if (!N) { + goto failJ0; + } + + if (!(R = concat_strings(3, A.str, ".", N))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + R = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(A); + free(N); +} + +statement_args_maybe(R) ::= . { + R.have = 1; + NCDValue_InitList(&R.v); +} + +statement_args_maybe(R) ::= list_contents(A). { + R = A; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValue_InitList(&R.v); + + if (!NCDValue_ListPrepend(&R.v, A.v)) { + goto failL1; + } + A.have = 0; + + R.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&R.v); +failL0: + R.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(A); +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&N.v, A.v)) { + goto failM0; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneM; + +failM0: + R.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(A); + free_value(N); +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + R.have = 1; + NCDValue_InitList(&R.v); +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValue_InitMap(&R.v); + + if (!NCDValue_MapPrepend(&R.v, A.v, B.v)) { + goto failS1; + } + A.have = 0; + B.have = 0; + + R.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&R.v); +failS0: + R.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(A); + free_value(B); +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&N.v, A.v, B.v)) { + goto failT0; + } + A.have = 0; + B.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneT; + +failT0: + R.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(A); + free_value(B); + free_value(N); +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + R.have = 1; + NCDValue_InitMap(&R.v); +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) { + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(A); +} + +value(R) ::= dotted_name(A). { + if (!A) { + goto failV0; + } + + if (!NCDValue_InitVar(&R.v, A)) { + goto failV0; + } + + R.have = 1; + goto doneV; + +failV0: + R.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} + +name_maybe(R) ::= . { + R = NULL; +} + +name_maybe(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +process_or_template(R) ::= PROCESS. { + R = 0; +} + +process_or_template(R) ::= TEMPLATE. { + R = 1; +} diff --git a/external/badvpn_dns/generated/NCDValParser_parse.c b/external/badvpn_dns/generated/NCDValParser_parse.c new file mode 100644 index 00000000..f7039cca --- /dev/null +++ b/external/badvpn_dns/generated/NCDValParser_parse.c @@ -0,0 +1,1119 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +#define YYCODETYPE unsigned char +#define YYNOCODE 16 +#define YYACTIONTYPE unsigned char +#define ParseTOKENTYPE struct token +typedef union { + int yyinit; + ParseTOKENTYPE yy0; + struct value yy1; +} YYMINORTYPE; +#ifndef YYSTACKDEPTH +#define YYSTACKDEPTH 0 +#endif +#define ParseARG_SDECL struct parser_state *parser_out ; +#define ParseARG_PDECL , struct parser_state *parser_out +#define ParseARG_FETCH struct parser_state *parser_out = yypParser->parser_out +#define ParseARG_STORE yypParser->parser_out = parser_out +#define YYNSTATE 21 +#define YYNRULE 12 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static const YYACTIONTYPE yy_action[] = { + /* 0 */ 15, 21, 16, 6, 34, 1, 19, 3, 2, 15, + /* 10 */ 14, 16, 9, 11, 15, 5, 16, 7, 18, 1, + /* 20 */ 35, 20, 2, 17, 14, 15, 10, 16, 8, 12, + /* 30 */ 15, 4, 16, 7, 15, 13, 16, 8, 1, 35, + /* 40 */ 35, 2, 35, 14, +}; +static const YYCODETYPE yy_lookahead[] = { + /* 0 */ 10, 0, 12, 13, 14, 2, 3, 1, 5, 10, + /* 10 */ 7, 12, 13, 9, 10, 4, 12, 13, 6, 2, + /* 20 */ 15, 3, 5, 6, 7, 10, 11, 12, 13, 9, + /* 30 */ 10, 1, 12, 13, 10, 11, 12, 13, 2, 15, + /* 40 */ 15, 5, 15, 7, +}; +#define YY_SHIFT_USE_DFLT (-1) +#define YY_SHIFT_MAX 11 +static const signed char yy_shift_ofst[] = { + /* 0 */ 36, 3, 17, 36, 36, 36, 1, 6, 11, 30, + /* 10 */ 12, 18, +}; +#define YY_REDUCE_USE_DFLT (-11) +#define YY_REDUCE_MAX 5 +static const signed char yy_reduce_ofst[] = { + /* 0 */ -10, 4, 15, 20, 24, -1, +}; +static const YYACTIONTYPE yy_default[] = { + /* 0 */ 33, 33, 33, 33, 33, 33, 33, 22, 33, 26, + /* 10 */ 33, 33, 23, 27, 30, 31, 32, 28, 29, 24, + /* 20 */ 25, +}; +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { + "$", "COMMA", "CURLY_OPEN", "CURLY_CLOSE", + "COLON", "BRACKET_OPEN", "BRACKET_CLOSE", "STRING", + "error", "list_contents", "list", "map_contents", + "map", "value", "input", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { + /* 0 */ "input ::= value", + /* 1 */ "list_contents ::= value", + /* 2 */ "list_contents ::= value COMMA list_contents", + /* 3 */ "list ::= CURLY_OPEN CURLY_CLOSE", + /* 4 */ "list ::= CURLY_OPEN list_contents CURLY_CLOSE", + /* 5 */ "map_contents ::= value COLON value", + /* 6 */ "map_contents ::= value COLON value COMMA map_contents", + /* 7 */ "map ::= BRACKET_OPEN BRACKET_CLOSE", + /* 8 */ "map ::= BRACKET_OPEN map_contents BRACKET_CLOSE", + /* 9 */ "value ::= STRING", + /* 10 */ "value ::= list", + /* 11 */ "value ::= map", +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + /* TERMINAL Destructor */ + case 1: /* COMMA */ + case 2: /* CURLY_OPEN */ + case 3: /* CURLY_CLOSE */ + case 4: /* COLON */ + case 5: /* BRACKET_OPEN */ + case 6: /* BRACKET_CLOSE */ + case 7: /* STRING */ +{ +#line 37 "NCDValParser_parse.y" + free_token((yypminor->yy0)); +#line 377 "NCDValParser_parse.c" +} + break; + case 9: /* list_contents */ +{ +#line 47 "NCDValParser_parse.y" + (void)parser_out; +#line 384 "NCDValParser_parse.c" +} + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +#line 58 "NCDValParser_parse.y" + + if (yypMinor) { + free_token(yypMinor->yy0); + } +#line 562 "NCDValParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 14, 1 }, + { 9, 1 }, + { 9, 3 }, + { 10, 2 }, + { 10, 3 }, + { 11, 3 }, + { 11, 5 }, + { 12, 2 }, + { 12, 3 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ + case 0: /* input ::= value */ +#line 64 "NCDValParser_parse.y" +{ + if (!yymsp[0].minor.yy1.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, yymsp[0].minor.yy1.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} +#line 705 "NCDValParser_parse.c" + break; + case 1: /* list_contents ::= value */ +#line 83 "NCDValParser_parse.y" +{ + if (!yymsp[0].minor.yy1.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &yygotominor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + yygotominor.yy1.have = 1; + goto doneL; + +failL0: + yygotominor.yy1.have = 0; +doneL:; +} +#line 728 "NCDValParser_parse.c" + break; + case 2: /* list_contents ::= value COMMA list_contents */ +#line 103 "NCDValParser_parse.y" +{ + if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + yygotominor.yy1.have = 1; + yygotominor.yy1.v = yymsp[0].minor.yy1.v; + goto doneM; + +failM0: + yygotominor.yy1.have = 0; +doneM:; + yy_destructor(yypParser,1,&yymsp[-1].minor); +} +#line 751 "NCDValParser_parse.c" + break; + case 3: /* list ::= CURLY_OPEN CURLY_CLOSE */ +#line 122 "NCDValParser_parse.y" +{ + NCDValCons_NewList(&parser_out->cons, &yygotominor.yy1.v); + yygotominor.yy1.have = 1; + yy_destructor(yypParser,2,&yymsp[-1].minor); + yy_destructor(yypParser,3,&yymsp[0].minor); +} +#line 761 "NCDValParser_parse.c" + break; + case 4: /* list ::= CURLY_OPEN list_contents CURLY_CLOSE */ +#line 127 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[-1].minor.yy1; + yy_destructor(yypParser,2,&yymsp[-2].minor); + yy_destructor(yypParser,3,&yymsp[0].minor); +} +#line 770 "NCDValParser_parse.c" + break; + case 5: /* map_contents ::= value COLON value */ +#line 131 "NCDValParser_parse.y" +{ + if (!yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &yygotominor.yy1.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &yygotominor.yy1.v, yymsp[-2].minor.yy1.v, yymsp[0].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + yygotominor.yy1.have = 1; + goto doneS; + +failS0: + yygotominor.yy1.have = 0; +doneS:; + yy_destructor(yypParser,4,&yymsp[-1].minor); +} +#line 794 "NCDValParser_parse.c" + break; + case 6: /* map_contents ::= value COLON value COMMA map_contents */ +#line 151 "NCDValParser_parse.y" +{ + if (!yymsp[-4].minor.yy1.have || !yymsp[-2].minor.yy1.have || !yymsp[0].minor.yy1.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &yymsp[0].minor.yy1.v, yymsp[-4].minor.yy1.v, yymsp[-2].minor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + yygotominor.yy1.have = 1; + yygotominor.yy1.v = yymsp[0].minor.yy1.v; + goto doneT; + +failT0: + yygotominor.yy1.have = 0; +doneT:; + yy_destructor(yypParser,4,&yymsp[-3].minor); + yy_destructor(yypParser,1,&yymsp[-1].minor); +} +#line 818 "NCDValParser_parse.c" + break; + case 7: /* map ::= BRACKET_OPEN BRACKET_CLOSE */ +#line 170 "NCDValParser_parse.y" +{ + NCDValCons_NewMap(&parser_out->cons, &yygotominor.yy1.v); + yygotominor.yy1.have = 1; + yy_destructor(yypParser,5,&yymsp[-1].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 828 "NCDValParser_parse.c" + break; + case 8: /* map ::= BRACKET_OPEN map_contents BRACKET_CLOSE */ +#line 175 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[-1].minor.yy1; + yy_destructor(yypParser,5,&yymsp[-2].minor); + yy_destructor(yypParser,6,&yymsp[0].minor); +} +#line 837 "NCDValParser_parse.c" + break; + case 9: /* value ::= STRING */ +#line 179 "NCDValParser_parse.y" +{ + ASSERT(yymsp[0].minor.yy0.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)yymsp[0].minor.yy0.str, yymsp[0].minor.yy0.len, &yygotominor.yy1.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + yygotominor.yy1.have = 1; + goto doneU; + +failU0: + yygotominor.yy1.have = 0; +doneU:; + free_token(yymsp[0].minor.yy0); +} +#line 857 "NCDValParser_parse.c" + break; + case 10: /* value ::= list */ + case 11: /* value ::= map */ yytestcase(yyruleno==11); +#line 196 "NCDValParser_parse.y" +{ + yygotominor.yy1 = yymsp[0].minor.yy1; +} +#line 865 "NCDValParser_parse.c" + break; + default: + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +#line 53 "NCDValParser_parse.y" + + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +#line 930 "NCDValParser_parse.c" + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/external/badvpn_dns/generated/NCDValParser_parse.h b/external/badvpn_dns/generated/NCDValParser_parse.h new file mode 100644 index 00000000..fd19b494 --- /dev/null +++ b/external/badvpn_dns/generated/NCDValParser_parse.h @@ -0,0 +1,7 @@ +#define COMMA 1 +#define CURLY_OPEN 2 +#define CURLY_CLOSE 3 +#define COLON 4 +#define BRACKET_OPEN 5 +#define BRACKET_CLOSE 6 +#define STRING 7 diff --git a/external/badvpn_dns/generated/NCDValParser_parse.out b/external/badvpn_dns/generated/NCDValParser_parse.out new file mode 100644 index 00000000..9f42273f --- /dev/null +++ b/external/badvpn_dns/generated/NCDValParser_parse.out @@ -0,0 +1,217 @@ +State 0: + input ::= * value + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map shift 16 + value shift 6 + input accept + +State 1: + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= CURLY_OPEN * CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + list ::= CURLY_OPEN * list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + CURLY_CLOSE shift 19 + BRACKET_OPEN shift 2 + STRING shift 14 + list_contents shift 11 + list shift 15 + map shift 16 + value shift 7 + +State 2: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= BRACKET_OPEN * BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + map ::= BRACKET_OPEN * map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + BRACKET_CLOSE shift 17 + STRING shift 14 + list shift 15 + map_contents shift 10 + map shift 16 + value shift 8 + +State 3: + list_contents ::= * value + list_contents ::= * value COMMA list_contents + list_contents ::= value COMMA * list_contents + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list_contents shift 12 + list shift 15 + map shift 16 + value shift 7 + +State 4: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= * value COLON value + map_contents ::= * value COLON value COMMA map_contents + map_contents ::= value COLON value COMMA * map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map_contents shift 13 + map shift 16 + value shift 8 + +State 5: + list ::= * CURLY_OPEN CURLY_CLOSE + list ::= * CURLY_OPEN list_contents CURLY_CLOSE + map_contents ::= value COLON * value + map_contents ::= value COLON * value COMMA map_contents + map ::= * BRACKET_OPEN BRACKET_CLOSE + map ::= * BRACKET_OPEN map_contents BRACKET_CLOSE + value ::= * STRING + value ::= * list + value ::= * map + + CURLY_OPEN shift 1 + BRACKET_OPEN shift 2 + STRING shift 14 + list shift 15 + map shift 16 + value shift 9 + +State 6: + (0) input ::= value * + + $ reduce 0 + +State 7: + (1) list_contents ::= value * + list_contents ::= value * COMMA list_contents + + COMMA shift 3 + {default} reduce 1 + +State 8: + map_contents ::= value * COLON value + map_contents ::= value * COLON value COMMA map_contents + + COLON shift 5 + +State 9: + (5) map_contents ::= value COLON value * + map_contents ::= value COLON value * COMMA map_contents + + COMMA shift 4 + {default} reduce 5 + +State 10: + map ::= BRACKET_OPEN map_contents * BRACKET_CLOSE + + BRACKET_CLOSE shift 18 + +State 11: + list ::= CURLY_OPEN list_contents * CURLY_CLOSE + + CURLY_CLOSE shift 20 + +State 12: + (2) list_contents ::= value COMMA list_contents * + + {default} reduce 2 + +State 13: + (6) map_contents ::= value COLON value COMMA map_contents * + + {default} reduce 6 + +State 14: + (9) value ::= STRING * + + {default} reduce 9 + +State 15: + (10) value ::= list * + + {default} reduce 10 + +State 16: + (11) value ::= map * + + {default} reduce 11 + +State 17: + (7) map ::= BRACKET_OPEN BRACKET_CLOSE * + + {default} reduce 7 + +State 18: + (8) map ::= BRACKET_OPEN map_contents BRACKET_CLOSE * + + {default} reduce 8 + +State 19: + (3) list ::= CURLY_OPEN CURLY_CLOSE * + + {default} reduce 3 + +State 20: + (4) list ::= CURLY_OPEN list_contents CURLY_CLOSE * + + {default} reduce 4 + +---------------------------------------------------- +Symbols: + 0: $: + 1: COMMA + 2: CURLY_OPEN + 3: CURLY_CLOSE + 4: COLON + 5: BRACKET_OPEN + 6: BRACKET_CLOSE + 7: STRING + 8: error: + 9: list_contents: CURLY_OPEN BRACKET_OPEN STRING + 10: list: CURLY_OPEN + 11: map_contents: CURLY_OPEN BRACKET_OPEN STRING + 12: map: BRACKET_OPEN + 13: value: CURLY_OPEN BRACKET_OPEN STRING + 14: input: CURLY_OPEN BRACKET_OPEN STRING diff --git a/external/badvpn_dns/generated/NCDValParser_parse.y b/external/badvpn_dns/generated/NCDValParser_parse.y new file mode 100644 index 00000000..ced123d3 --- /dev/null +++ b/external/badvpn_dns/generated/NCDValParser_parse.y @@ -0,0 +1,202 @@ +/** + * @file NCDConfigParser_parse.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// argument for passing state to parser hooks +%extra_argument { struct parser_state *parser_out } + +// type of structure representing tokens +%token_type { struct token } + +// token destructor frees extra memory allocated for tokens +%token_destructor { free_token($$); } + +// types of nonterminals +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } + +// mention parser_out in some destructor to and avoid unused variable warning +%destructor list_contents { (void)parser_out; } + +// try to dynamically grow the parse stack +%stack_size 0 + +// on syntax error, set the corresponding error flag +%syntax_error { + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= value(A). { + if (!A.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &R.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + R.have = 1; + goto doneL; + +failL0: + R.have = 0; +doneL:; +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + R.have = 1; + R.v = N.v; + goto doneM; + +failM0: + R.have = 0; +doneM:; +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + NCDValCons_NewList(&parser_out->cons, &R.v); + R.have = 1; +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &R.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + R.have = 1; + goto doneS; + +failS0: + R.have = 0; +doneS:; +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + R.have = 1; + R.v = N.v; + goto doneT; + +failT0: + R.have = 0; +doneT:; +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + NCDValCons_NewMap(&parser_out->cons, &R.v); + R.have = 1; +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; +doneU:; + free_token(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} diff --git a/external/badvpn_dns/generated/bison_BPredicate.c b/external/badvpn_dns/generated/bison_BPredicate.c new file mode 100644 index 00000000..5a0a6056 --- /dev/null +++ b/external/badvpn_dns/generated/bison_BPredicate.c @@ -0,0 +1,2168 @@ +/* A Bison parser, made by GNU Bison 2.7.12-4996. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.7.12-4996" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* Copy the first part of user declarations. */ +/* Line 371 of yacc.c */ +#line 34 "predicate/BPredicate.y" + + +#include + +#include +#include + +#define YYLEX_PARAM scanner + +static struct predicate_node * make_constant (int val) +{ + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + return NULL; + } + + n->type = NODE_CONSTANT; + n->constant.val = val; + + return n; +} + +static struct predicate_node * make_negation (struct predicate_node *op) +{ + if (!op) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_NEG; + n->neg.op = op; + + return n; + +fail: + if (op) { + free_predicate_node(op); + } + return NULL; +} + +static struct predicate_node * make_conjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_CONJUNCT; + n->conjunct.op1 = op1; + n->conjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_disjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_DISJUNCT; + n->disjunct.op1 = op1; + n->disjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_function (char *name, struct arguments_node *args, int need_args) +{ + if (!name || (!args && need_args)) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_FUNCTION; + n->function.name = name; + n->function.args = args; + + return n; + +fail: + if (name) { + free(name); + } + if (args) { + free_arguments_node(args); + } + return NULL; +} + +static struct arguments_node * make_arguments (struct arguments_arg arg, struct arguments_node *next, int need_next) +{ + if (arg.type == ARGUMENT_INVALID || (!next && need_next)) { + goto fail; + } + + struct arguments_node *n = (struct arguments_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->arg = arg; + n->next = next; + + return n; + +fail: + free_argument(arg); + if (next) { + free_arguments_node(next); + } + return NULL; +} + +static struct arguments_arg make_argument_predicate (struct predicate_node *pr) +{ + struct arguments_arg ret; + + if (!pr) { + goto fail; + } + + ret.type = ARGUMENT_PREDICATE; + ret.predicate = pr; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +static struct arguments_arg make_argument_string (char *string) +{ + struct arguments_arg ret; + + if (!string) { + goto fail; + } + + ret.type = ARGUMENT_STRING; + ret.string = string; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + + +/* Line 371 of yacc.c */ +#line 256 "generated//bison_BPredicate.c" + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "bison_BPredicate.h". */ +#ifndef YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +# define YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + STRING = 258, + NAME = 259, + PEER1_NAME = 260, + PEER2_NAME = 261, + AND = 262, + OR = 263, + NOT = 264, + SPAR = 265, + EPAR = 266, + CONSTANT_TRUE = 267, + CONSTANT_FALSE = 268, + COMMA = 269 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 387 of yacc.c */ +#line 227 "predicate/BPredicate.y" + + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; + + +/* Line 387 of yacc.c */ +#line 322 "generated//bison_BPredicate.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void *scanner, struct predicate_node **result); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + +/* Line 390 of yacc.c */ +#line 362 "generated//bison_BPredicate.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(N) (N) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 17 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 37 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 15 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 11 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 20 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 31 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 269 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 5, 7, 9, 11, 13, 15, 17, + 19, 21, 25, 28, 32, 36, 40, 45, 47, 51, + 53 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 16, 0, -1, 17, -1, 18, -1, 19, -1, 20, + -1, 21, -1, 22, -1, 23, -1, 12, -1, 13, + -1, 10, 17, 11, -1, 9, 17, -1, 17, 7, + 17, -1, 17, 8, 17, -1, 4, 10, 11, -1, + 4, 10, 24, 11, -1, 25, -1, 25, 14, 24, + -1, 17, -1, 3, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 276, 276, 281, 281, 281, 281, 281, 281, 284, + 288, 294, 300, 306, 312, 318, 322, 328, 332, 338, + 342 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "STRING", "NAME", "PEER1_NAME", + "PEER2_NAME", "AND", "OR", "NOT", "SPAR", "EPAR", "CONSTANT_TRUE", + "CONSTANT_FALSE", "COMMA", "$accept", "input", "predicate", "constant", + "parentheses", "neg", "conjunct", "disjunct", "function", "arguments", + "argument", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 15, 16, 17, 17, 17, 17, 17, 17, 18, + 18, 19, 20, 21, 22, 23, 23, 24, 24, 25, + 25 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 2, 3, 3, 3, 4, 1, 3, 1, + 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 9, 10, 0, 2, 3, 4, + 5, 6, 7, 8, 0, 12, 0, 1, 0, 0, + 20, 15, 19, 0, 17, 11, 13, 14, 16, 0, + 18 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 6, 22, 8, 9, 10, 11, 12, 13, 23, + 24 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -10 +static const yytype_int8 yypact[] = +{ + 19, -9, 19, 19, -10, -10, 8, -1, -10, -10, + -10, -10, -10, -10, 1, -10, 26, -10, 19, 19, + -10, -10, -1, -2, 3, -10, -10, 13, -10, 12, + -10 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -10, -10, 0, -10, -10, -10, -10, -10, -10, -3, + -10 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 7, 14, 15, 16, 20, 1, 18, 19, 17, 28, + 2, 3, 21, 4, 5, 20, 1, 29, 26, 27, + 18, 2, 3, 1, 4, 5, 30, 0, 2, 3, + 0, 4, 5, 18, 19, 0, 0, 25 +}; + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-10))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 0, 10, 2, 3, 3, 4, 7, 8, 0, 11, + 9, 10, 11, 12, 13, 3, 4, 14, 18, 19, + 7, 9, 10, 4, 12, 13, 29, -1, 9, 10, + -1, 12, 13, 7, 8, -1, -1, 11 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 4, 9, 10, 12, 13, 16, 17, 18, 19, + 20, 21, 22, 23, 10, 17, 17, 0, 7, 8, + 3, 11, 17, 24, 25, 11, 17, 17, 11, 14, + 24 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, scanner, result, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +__attribute__((__unused__)) +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static unsigned +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +#else +static unsigned +yy_location_print_ (yyo, yylocp) + FILE *yyo; + YYLTYPE const * const yylocp; +#endif +{ + unsigned res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += fprintf (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += fprintf (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += fprintf (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += fprintf (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += fprintf (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, &yylloc) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, Location, scanner, result); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; + YYUSE (yylocationp); + YYUSE (scanner); + YYUSE (result); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + YYLTYPE const * const yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + YY_LOCATION_PRINT (yyoutput, *yylocationp); + YYFPRINTF (yyoutput, ": "); + yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, scanner, result); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, void *scanner, struct predicate_node **result) +#else +static void +yy_reduce_print (yyvsp, yylsp, yyrule, scanner, result) + YYSTYPE *yyvsp; + YYLTYPE *yylsp; + int yyrule; + void *scanner; + struct predicate_node **result; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , &(yylsp[(yyi + 1) - (yynrhs)]) , scanner, result); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, yylsp, Rule, scanner, result); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, void *scanner, struct predicate_node **result) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, yylocationp, scanner, result) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + YYLTYPE *yylocationp; + void *scanner; + struct predicate_node **result; +#endif +{ + YYUSE (yyvaluep); + YYUSE (yylocationp); + YYUSE (scanner); + YYUSE (result); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 3: /* STRING */ +/* Line 1393 of yacc.c */ +#line 240 "predicate/BPredicate.y" + { + free(((*yyvaluep).text)); +}; +/* Line 1393 of yacc.c */ +#line 1391 "generated//bison_BPredicate.c" + break; + case 4: /* NAME */ +/* Line 1393 of yacc.c */ +#line 240 "predicate/BPredicate.y" + { + free(((*yyvaluep).text)); +}; +/* Line 1393 of yacc.c */ +#line 1400 "generated//bison_BPredicate.c" + break; + case 17: /* predicate */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1411 "generated//bison_BPredicate.c" + break; + case 18: /* constant */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1422 "generated//bison_BPredicate.c" + break; + case 19: /* parentheses */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1433 "generated//bison_BPredicate.c" + break; + case 20: /* neg */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1444 "generated//bison_BPredicate.c" + break; + case 21: /* conjunct */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1455 "generated//bison_BPredicate.c" + break; + case 22: /* disjunct */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1466 "generated//bison_BPredicate.c" + break; + case 23: /* function */ +/* Line 1393 of yacc.c */ +#line 250 "predicate/BPredicate.y" + { + if (((*yyvaluep).node)) { + free_predicate_node(((*yyvaluep).node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1477 "generated//bison_BPredicate.c" + break; + case 24: /* arguments */ +/* Line 1393 of yacc.c */ +#line 257 "predicate/BPredicate.y" + { + if (((*yyvaluep).arg_node)) { + free_arguments_node(((*yyvaluep).arg_node)); + } +}; +/* Line 1393 of yacc.c */ +#line 1488 "generated//bison_BPredicate.c" + break; + case 25: /* argument */ +/* Line 1393 of yacc.c */ +#line 264 "predicate/BPredicate.y" + { + free_argument(((*yyvaluep).arg_arg)); +}; +/* Line 1393 of yacc.c */ +#line 1497 "generated//bison_BPredicate.c" + break; + + default: + break; + } +} + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *scanner, struct predicate_node **result) +#else +int +yyparse (scanner, result) + void *scanner; + struct predicate_node **result; +#endif +#endif +{ +/* The lookahead symbol. */ +int yychar; + + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +static YYSTYPE yyval_default; +# define YY_INITIAL_VALUE(Value) = Value +#endif +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); + +/* Location data for the lookahead symbol. */ +YYLTYPE yylloc = yyloc_default; + + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + `yyls': related to locations. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls; + YYLTYPE *yylsp; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yylsp = yyls = yylsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + + yyls = yyls1; + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +/* Line 1787 of yacc.c */ +#line 276 "predicate/BPredicate.y" + { + *result = (yyvsp[(1) - (1)].node); + } + break; + + case 9: +/* Line 1787 of yacc.c */ +#line 284 "predicate/BPredicate.y" + { + (yyval.node) = make_constant(1); + } + break; + + case 10: +/* Line 1787 of yacc.c */ +#line 288 "predicate/BPredicate.y" + { + (yyval.node) = make_constant(0); + } + break; + + case 11: +/* Line 1787 of yacc.c */ +#line 294 "predicate/BPredicate.y" + { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 12: +/* Line 1787 of yacc.c */ +#line 300 "predicate/BPredicate.y" + { + (yyval.node) = make_negation((yyvsp[(2) - (2)].node)); + } + break; + + case 13: +/* Line 1787 of yacc.c */ +#line 306 "predicate/BPredicate.y" + { + (yyval.node) = make_conjunction((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 14: +/* Line 1787 of yacc.c */ +#line 312 "predicate/BPredicate.y" + { + (yyval.node) = make_disjunction((yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 15: +/* Line 1787 of yacc.c */ +#line 318 "predicate/BPredicate.y" + { + (yyval.node) = make_function((yyvsp[(1) - (3)].text), NULL, 0); + } + break; + + case 16: +/* Line 1787 of yacc.c */ +#line 322 "predicate/BPredicate.y" + { + (yyval.node) = make_function((yyvsp[(1) - (4)].text), (yyvsp[(3) - (4)].arg_node), 1); + } + break; + + case 17: +/* Line 1787 of yacc.c */ +#line 328 "predicate/BPredicate.y" + { + (yyval.arg_node) = make_arguments((yyvsp[(1) - (1)].arg_arg), NULL, 0); + } + break; + + case 18: +/* Line 1787 of yacc.c */ +#line 332 "predicate/BPredicate.y" + { + (yyval.arg_node) = make_arguments((yyvsp[(1) - (3)].arg_arg), (yyvsp[(3) - (3)].arg_node), 1); + } + break; + + case 19: +/* Line 1787 of yacc.c */ +#line 338 "predicate/BPredicate.y" + { + (yyval.arg_arg) = make_argument_predicate((yyvsp[(1) - (1)].node)); + } + break; + + case 20: +/* Line 1787 of yacc.c */ +#line 342 "predicate/BPredicate.y" + { + (yyval.arg_arg) = make_argument_string((yyvsp[(1) - (1)].text)); + } + break; + + +/* Line 1787 of yacc.c */ +#line 1932 "generated//bison_BPredicate.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (&yylloc, scanner, result, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (&yylloc, scanner, result, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + yyerror_range[1] = yylloc; + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, scanner, result); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + yyerror_range[1] = yylsp[1-yylen]; + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + yystos[yystate], yyvsp, yylsp, scanner, result); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + /* Using YYLLOC is tempting, but would change the location of + the lookahead. YYLOC is available though. */ + YYLLOC_DEFAULT (yyloc, yyerror_range, 2); + *++yylsp = yyloc; + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, scanner, result, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, scanner, result); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, yylsp, scanner, result); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + diff --git a/external/badvpn_dns/generated/bison_BPredicate.h b/external/badvpn_dns/generated/bison_BPredicate.h new file mode 100644 index 00000000..2bf36cee --- /dev/null +++ b/external/badvpn_dns/generated/bison_BPredicate.h @@ -0,0 +1,114 @@ +/* A Bison parser, made by GNU Bison 2.7.12-4996. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + 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 3 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +# define YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + STRING = 258, + NAME = 259, + PEER1_NAME = 260, + PEER2_NAME = 261, + AND = 262, + OR = 263, + NOT = 264, + SPAR = 265, + EPAR = 266, + CONSTANT_TRUE = 267, + CONSTANT_FALSE = 268, + COMMA = 269 + }; +#endif + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ +/* Line 2053 of yacc.c */ +#line 227 "predicate/BPredicate.y" + + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; + + +/* Line 2053 of yacc.c */ +#line 80 "generated//bison_BPredicate.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +} YYLTYPE; +# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void *scanner, struct predicate_node **result); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_GENERATED_BISON_BPREDICATE_H_INCLUDED */ diff --git a/external/badvpn_dns/generated/blog_channel_BArpProbe.h b/external/badvpn_dns/generated/blog_channel_BArpProbe.h new file mode 100644 index 00000000..f168e638 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BArpProbe.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BArpProbe diff --git a/external/badvpn_dns/generated/blog_channel_BConnection.h b/external/badvpn_dns/generated/blog_channel_BConnection.h new file mode 100644 index 00000000..8447db02 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BConnection diff --git a/external/badvpn_dns/generated/blog_channel_BDHCPClient.h b/external/badvpn_dns/generated/blog_channel_BDHCPClient.h new file mode 100644 index 00000000..aacf34db --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BDHCPClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDHCPClient diff --git a/external/badvpn_dns/generated/blog_channel_BDHCPClientCore.h b/external/badvpn_dns/generated/blog_channel_BDHCPClientCore.h new file mode 100644 index 00000000..64fc74d3 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BDHCPClientCore.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDHCPClientCore diff --git a/external/badvpn_dns/generated/blog_channel_BDatagram.h b/external/badvpn_dns/generated/blog_channel_BDatagram.h new file mode 100644 index 00000000..d95cf245 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BDatagram.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BDatagram diff --git a/external/badvpn_dns/generated/blog_channel_BEncryption.h b/external/badvpn_dns/generated/blog_channel_BEncryption.h new file mode 100644 index 00000000..6991f370 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BEncryption.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BEncryption diff --git a/external/badvpn_dns/generated/blog_channel_BInputProcess.h b/external/badvpn_dns/generated/blog_channel_BInputProcess.h new file mode 100644 index 00000000..f65f715a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BInputProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BInputProcess diff --git a/external/badvpn_dns/generated/blog_channel_BLockReactor.h b/external/badvpn_dns/generated/blog_channel_BLockReactor.h new file mode 100644 index 00000000..5aab6d41 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BLockReactor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BLockReactor diff --git a/external/badvpn_dns/generated/blog_channel_BNetwork.h b/external/badvpn_dns/generated/blog_channel_BNetwork.h new file mode 100644 index 00000000..c5e3bc1e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BNetwork.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BNetwork diff --git a/external/badvpn_dns/generated/blog_channel_BPredicate.h b/external/badvpn_dns/generated/blog_channel_BPredicate.h new file mode 100644 index 00000000..1a683f13 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BPredicate.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BPredicate diff --git a/external/badvpn_dns/generated/blog_channel_BProcess.h b/external/badvpn_dns/generated/blog_channel_BProcess.h new file mode 100644 index 00000000..e11e5a62 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BProcess diff --git a/external/badvpn_dns/generated/blog_channel_BReactor.h b/external/badvpn_dns/generated/blog_channel_BReactor.h new file mode 100644 index 00000000..d111dc79 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BReactor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BReactor diff --git a/external/badvpn_dns/generated/blog_channel_BSSLConnection.h b/external/badvpn_dns/generated/blog_channel_BSSLConnection.h new file mode 100644 index 00000000..bd55826b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BSSLConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSSLConnection diff --git a/external/badvpn_dns/generated/blog_channel_BSignal.h b/external/badvpn_dns/generated/blog_channel_BSignal.h new file mode 100644 index 00000000..2820ebce --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSignal diff --git a/external/badvpn_dns/generated/blog_channel_BSocksClient.h b/external/badvpn_dns/generated/blog_channel_BSocksClient.h new file mode 100644 index 00000000..72086fab --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BSocksClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BSocksClient diff --git a/external/badvpn_dns/generated/blog_channel_BTap.h b/external/badvpn_dns/generated/blog_channel_BTap.h new file mode 100644 index 00000000..ab3e951e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BTap.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTap diff --git a/external/badvpn_dns/generated/blog_channel_BThreadSignal.h b/external/badvpn_dns/generated/blog_channel_BThreadSignal.h new file mode 100644 index 00000000..f39fc368 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BThreadSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BThreadSignal diff --git a/external/badvpn_dns/generated/blog_channel_BThreadWork.h b/external/badvpn_dns/generated/blog_channel_BThreadWork.h new file mode 100644 index 00000000..f68383c2 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BThreadWork.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BThreadWork diff --git a/external/badvpn_dns/generated/blog_channel_BTime.h b/external/badvpn_dns/generated/blog_channel_BTime.h new file mode 100644 index 00000000..b323ee76 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BTime.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BTime diff --git a/external/badvpn_dns/generated/blog_channel_BUnixSignal.h b/external/badvpn_dns/generated/blog_channel_BUnixSignal.h new file mode 100644 index 00000000..914b21b8 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_BUnixSignal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BUnixSignal diff --git a/external/badvpn_dns/generated/blog_channel_DPReceive.h b/external/badvpn_dns/generated/blog_channel_DPReceive.h new file mode 100644 index 00000000..99889b56 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_DPReceive.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DPReceive diff --git a/external/badvpn_dns/generated/blog_channel_DPRelay.h b/external/badvpn_dns/generated/blog_channel_DPRelay.h new file mode 100644 index 00000000..bc0153be --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_DPRelay.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DPRelay diff --git a/external/badvpn_dns/generated/blog_channel_DataProto.h b/external/badvpn_dns/generated/blog_channel_DataProto.h new file mode 100644 index 00000000..a6f900a3 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_DataProto.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DataProto diff --git a/external/badvpn_dns/generated/blog_channel_DatagramPeerIO.h b/external/badvpn_dns/generated/blog_channel_DatagramPeerIO.h new file mode 100644 index 00000000..16e37b5b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_DatagramPeerIO.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_DatagramPeerIO diff --git a/external/badvpn_dns/generated/blog_channel_FragmentProtoAssembler.h b/external/badvpn_dns/generated/blog_channel_FragmentProtoAssembler.h new file mode 100644 index 00000000..25289efb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_FragmentProtoAssembler.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_FragmentProtoAssembler diff --git a/external/badvpn_dns/generated/blog_channel_FrameDecider.h b/external/badvpn_dns/generated/blog_channel_FrameDecider.h new file mode 100644 index 00000000..5dbf3c46 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_FrameDecider.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_FrameDecider diff --git a/external/badvpn_dns/generated/blog_channel_LineBuffer.h b/external/badvpn_dns/generated/blog_channel_LineBuffer.h new file mode 100644 index 00000000..4286a742 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_LineBuffer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_LineBuffer diff --git a/external/badvpn_dns/generated/blog_channel_Listener.h b/external/badvpn_dns/generated/blog_channel_Listener.h new file mode 100644 index 00000000..f61bfb3d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_Listener.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_Listener diff --git a/external/badvpn_dns/generated/blog_channel_NCDBuildProgram.h b/external/badvpn_dns/generated/blog_channel_NCDBuildProgram.h new file mode 100644 index 00000000..1a6cdf91 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDBuildProgram.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDBuildProgram diff --git a/external/badvpn_dns/generated/blog_channel_NCDConfigParser.h b/external/badvpn_dns/generated/blog_channel_NCDConfigParser.h new file mode 100644 index 00000000..92d98d06 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDConfigParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDConfigParser diff --git a/external/badvpn_dns/generated/blog_channel_NCDConfigTokenizer.h b/external/badvpn_dns/generated/blog_channel_NCDConfigTokenizer.h new file mode 100644 index 00000000..0b3b6892 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDConfigTokenizer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDConfigTokenizer diff --git a/external/badvpn_dns/generated/blog_channel_NCDIfConfig.h b/external/badvpn_dns/generated/blog_channel_NCDIfConfig.h new file mode 100644 index 00000000..91bdbda5 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDIfConfig.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDIfConfig diff --git a/external/badvpn_dns/generated/blog_channel_NCDInterfaceMonitor.h b/external/badvpn_dns/generated/blog_channel_NCDInterfaceMonitor.h new file mode 100644 index 00000000..22c0f8d6 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDInterfaceMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDInterfaceMonitor diff --git a/external/badvpn_dns/generated/blog_channel_NCDModuleIndex.h b/external/badvpn_dns/generated/blog_channel_NCDModuleIndex.h new file mode 100644 index 00000000..34876642 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDModuleIndex.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDModuleIndex diff --git a/external/badvpn_dns/generated/blog_channel_NCDModuleProcess.h b/external/badvpn_dns/generated/blog_channel_NCDModuleProcess.h new file mode 100644 index 00000000..db34dcc0 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDModuleProcess.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDModuleProcess diff --git a/external/badvpn_dns/generated/blog_channel_NCDPlaceholderDb.h b/external/badvpn_dns/generated/blog_channel_NCDPlaceholderDb.h new file mode 100644 index 00000000..f1f1db2c --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDPlaceholderDb.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDPlaceholderDb diff --git a/external/badvpn_dns/generated/blog_channel_NCDRequest.h b/external/badvpn_dns/generated/blog_channel_NCDRequest.h new file mode 100644 index 00000000..5edc18a2 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDRequest.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRequest diff --git a/external/badvpn_dns/generated/blog_channel_NCDRequestClient.h b/external/badvpn_dns/generated/blog_channel_NCDRequestClient.h new file mode 100644 index 00000000..1e696d8c --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDRequestClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRequestClient diff --git a/external/badvpn_dns/generated/blog_channel_NCDRfkillMonitor.h b/external/badvpn_dns/generated/blog_channel_NCDRfkillMonitor.h new file mode 100644 index 00000000..56eba888 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDRfkillMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDRfkillMonitor diff --git a/external/badvpn_dns/generated/blog_channel_NCDUdevCache.h b/external/badvpn_dns/generated/blog_channel_NCDUdevCache.h new file mode 100644 index 00000000..088fc9b3 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDUdevCache.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevCache diff --git a/external/badvpn_dns/generated/blog_channel_NCDUdevManager.h b/external/badvpn_dns/generated/blog_channel_NCDUdevManager.h new file mode 100644 index 00000000..e9d63758 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDUdevManager.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevManager diff --git a/external/badvpn_dns/generated/blog_channel_NCDUdevMonitor.h b/external/badvpn_dns/generated/blog_channel_NCDUdevMonitor.h new file mode 100644 index 00000000..bd93249a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDUdevMonitor.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevMonitor diff --git a/external/badvpn_dns/generated/blog_channel_NCDUdevMonitorParser.h b/external/badvpn_dns/generated/blog_channel_NCDUdevMonitorParser.h new file mode 100644 index 00000000..a7d560f4 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDUdevMonitorParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDUdevMonitorParser diff --git a/external/badvpn_dns/generated/blog_channel_NCDVal.h b/external/badvpn_dns/generated/blog_channel_NCDVal.h new file mode 100644 index 00000000..f2b67c28 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDVal.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDVal diff --git a/external/badvpn_dns/generated/blog_channel_NCDValGenerator.h b/external/badvpn_dns/generated/blog_channel_NCDValGenerator.h new file mode 100644 index 00000000..193826bb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDValGenerator.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDValGenerator diff --git a/external/badvpn_dns/generated/blog_channel_NCDValParser.h b/external/badvpn_dns/generated/blog_channel_NCDValParser.h new file mode 100644 index 00000000..1d44acb7 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_NCDValParser.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_NCDValParser diff --git a/external/badvpn_dns/generated/blog_channel_PRStreamSink.h b/external/badvpn_dns/generated/blog_channel_PRStreamSink.h new file mode 100644 index 00000000..b70b61cd --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_PRStreamSink.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PRStreamSink diff --git a/external/badvpn_dns/generated/blog_channel_PRStreamSource.h b/external/badvpn_dns/generated/blog_channel_PRStreamSource.h new file mode 100644 index 00000000..e16d93db --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_PRStreamSource.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PRStreamSource diff --git a/external/badvpn_dns/generated/blog_channel_PacketProtoDecoder.h b/external/badvpn_dns/generated/blog_channel_PacketProtoDecoder.h new file mode 100644 index 00000000..fbfa5d81 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_PacketProtoDecoder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PacketProtoDecoder diff --git a/external/badvpn_dns/generated/blog_channel_PasswordListener.h b/external/badvpn_dns/generated/blog_channel_PasswordListener.h new file mode 100644 index 00000000..6ff0bb58 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_PasswordListener.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PasswordListener diff --git a/external/badvpn_dns/generated/blog_channel_PeerChat.h b/external/badvpn_dns/generated/blog_channel_PeerChat.h new file mode 100644 index 00000000..cadf2308 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_PeerChat.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_PeerChat diff --git a/external/badvpn_dns/generated/blog_channel_SPProtoDecoder.h b/external/badvpn_dns/generated/blog_channel_SPProtoDecoder.h new file mode 100644 index 00000000..09bf2599 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_SPProtoDecoder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SPProtoDecoder diff --git a/external/badvpn_dns/generated/blog_channel_ServerConnection.h b/external/badvpn_dns/generated/blog_channel_ServerConnection.h new file mode 100644 index 00000000..faea1ddd --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ServerConnection.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ServerConnection diff --git a/external/badvpn_dns/generated/blog_channel_SocksUdpGwClient.h b/external/badvpn_dns/generated/blog_channel_SocksUdpGwClient.h new file mode 100644 index 00000000..6ba39aed --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_SocksUdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SocksUdpGwClient diff --git a/external/badvpn_dns/generated/blog_channel_StreamPeerIO.h b/external/badvpn_dns/generated/blog_channel_StreamPeerIO.h new file mode 100644 index 00000000..0359736e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_StreamPeerIO.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_StreamPeerIO diff --git a/external/badvpn_dns/generated/blog_channel_UdpGwClient.h b/external/badvpn_dns/generated/blog_channel_UdpGwClient.h new file mode 100644 index 00000000..85303768 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_UdpGwClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_UdpGwClient diff --git a/external/badvpn_dns/generated/blog_channel_addr.h b/external/badvpn_dns/generated/blog_channel_addr.h new file mode 100644 index 00000000..512db286 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_addr diff --git a/external/badvpn_dns/generated/blog_channel_client.h b/external/badvpn_dns/generated/blog_channel_client.h new file mode 100644 index 00000000..c851b77b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_client.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_client diff --git a/external/badvpn_dns/generated/blog_channel_dostest_attacker.h b/external/badvpn_dns/generated/blog_channel_dostest_attacker.h new file mode 100644 index 00000000..b267c8f4 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_dostest_attacker.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_dostest_attacker diff --git a/external/badvpn_dns/generated/blog_channel_dostest_server.h b/external/badvpn_dns/generated/blog_channel_dostest_server.h new file mode 100644 index 00000000..8d3988e0 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_dostest_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_dostest_server diff --git a/external/badvpn_dns/generated/blog_channel_flooder.h b/external/badvpn_dns/generated/blog_channel_flooder.h new file mode 100644 index 00000000..94f595eb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_flooder.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_flooder diff --git a/external/badvpn_dns/generated/blog_channel_lwip.h b/external/badvpn_dns/generated/blog_channel_lwip.h new file mode 100644 index 00000000..fb5687df --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_lwip.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_lwip diff --git a/external/badvpn_dns/generated/blog_channel_ncd.h b/external/badvpn_dns/generated/blog_channel_ncd.h new file mode 100644 index 00000000..9bf29567 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd diff --git a/external/badvpn_dns/generated/blog_channel_ncd_alias.h b/external/badvpn_dns/generated/blog_channel_ncd_alias.h new file mode 100644 index 00000000..5b52bf26 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_alias.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_alias diff --git a/external/badvpn_dns/generated/blog_channel_ncd_arithmetic.h b/external/badvpn_dns/generated/blog_channel_ncd_arithmetic.h new file mode 100644 index 00000000..66c08a8e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_arithmetic.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_arithmetic diff --git a/external/badvpn_dns/generated/blog_channel_ncd_assert.h b/external/badvpn_dns/generated/blog_channel_ncd_assert.h new file mode 100644 index 00000000..21e4d419 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_assert.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_assert diff --git a/external/badvpn_dns/generated/blog_channel_ncd_backtrack.h b/external/badvpn_dns/generated/blog_channel_ncd_backtrack.h new file mode 100644 index 00000000..ea669f79 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_backtrack.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_backtrack diff --git a/external/badvpn_dns/generated/blog_channel_ncd_blocker.h b/external/badvpn_dns/generated/blog_channel_ncd_blocker.h new file mode 100644 index 00000000..a897b9f5 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_blocker.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_blocker diff --git a/external/badvpn_dns/generated/blog_channel_ncd_buffer.h b/external/badvpn_dns/generated/blog_channel_ncd_buffer.h new file mode 100644 index 00000000..64e44333 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_buffer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_buffer diff --git a/external/badvpn_dns/generated/blog_channel_ncd_call2.h b/external/badvpn_dns/generated/blog_channel_ncd_call2.h new file mode 100644 index 00000000..4b64608d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_call2.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_call2 diff --git a/external/badvpn_dns/generated/blog_channel_ncd_choose.h b/external/badvpn_dns/generated/blog_channel_ncd_choose.h new file mode 100644 index 00000000..a915036f --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_choose.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_choose diff --git a/external/badvpn_dns/generated/blog_channel_ncd_concat.h b/external/badvpn_dns/generated/blog_channel_ncd_concat.h new file mode 100644 index 00000000..8c54ccbb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_concat.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_concat diff --git a/external/badvpn_dns/generated/blog_channel_ncd_daemon.h b/external/badvpn_dns/generated/blog_channel_ncd_daemon.h new file mode 100644 index 00000000..0a3ae3fa --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_daemon.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_daemon diff --git a/external/badvpn_dns/generated/blog_channel_ncd_depend.h b/external/badvpn_dns/generated/blog_channel_ncd_depend.h new file mode 100644 index 00000000..ae1ff8e9 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_depend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_depend diff --git a/external/badvpn_dns/generated/blog_channel_ncd_depend_scope.h b/external/badvpn_dns/generated/blog_channel_ncd_depend_scope.h new file mode 100644 index 00000000..1168714d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_depend_scope.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_depend_scope diff --git a/external/badvpn_dns/generated/blog_channel_ncd_dynamic_depend.h b/external/badvpn_dns/generated/blog_channel_ncd_dynamic_depend.h new file mode 100644 index 00000000..7ff305ea --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_dynamic_depend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_dynamic_depend diff --git a/external/badvpn_dns/generated/blog_channel_ncd_exit.h b/external/badvpn_dns/generated/blog_channel_ncd_exit.h new file mode 100644 index 00000000..2d2e3af1 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_exit.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_exit diff --git a/external/badvpn_dns/generated/blog_channel_ncd_explode.h b/external/badvpn_dns/generated/blog_channel_ncd_explode.h new file mode 100644 index 00000000..b7dc820e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_explode.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_explode diff --git a/external/badvpn_dns/generated/blog_channel_ncd_file.h b/external/badvpn_dns/generated/blog_channel_ncd_file.h new file mode 100644 index 00000000..6cfa5a55 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_file.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_file diff --git a/external/badvpn_dns/generated/blog_channel_ncd_file_open.h b/external/badvpn_dns/generated/blog_channel_ncd_file_open.h new file mode 100644 index 00000000..dd4ecb5f --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_file_open.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_file_open diff --git a/external/badvpn_dns/generated/blog_channel_ncd_foreach.h b/external/badvpn_dns/generated/blog_channel_ncd_foreach.h new file mode 100644 index 00000000..430b2294 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_foreach.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_foreach diff --git a/external/badvpn_dns/generated/blog_channel_ncd_from_string.h b/external/badvpn_dns/generated/blog_channel_ncd_from_string.h new file mode 100644 index 00000000..e409fffa --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_from_string.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_from_string diff --git a/external/badvpn_dns/generated/blog_channel_ncd_getargs.h b/external/badvpn_dns/generated/blog_channel_ncd_getargs.h new file mode 100644 index 00000000..da7631d4 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_getargs.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_getargs diff --git a/external/badvpn_dns/generated/blog_channel_ncd_getenv.h b/external/badvpn_dns/generated/blog_channel_ncd_getenv.h new file mode 100644 index 00000000..4f290216 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_getenv.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_getenv diff --git a/external/badvpn_dns/generated/blog_channel_ncd_if.h b/external/badvpn_dns/generated/blog_channel_ncd_if.h new file mode 100644 index 00000000..11a09a2a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_if.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_if diff --git a/external/badvpn_dns/generated/blog_channel_ncd_imperative.h b/external/badvpn_dns/generated/blog_channel_ncd_imperative.h new file mode 100644 index 00000000..362df87a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_imperative.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_imperative diff --git a/external/badvpn_dns/generated/blog_channel_ncd_implode.h b/external/badvpn_dns/generated/blog_channel_ncd_implode.h new file mode 100644 index 00000000..5bb66d5b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_implode.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_implode diff --git a/external/badvpn_dns/generated/blog_channel_ncd_index.h b/external/badvpn_dns/generated/blog_channel_ncd_index.h new file mode 100644 index 00000000..666bbe9b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_index.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_index diff --git a/external/badvpn_dns/generated/blog_channel_ncd_list.h b/external/badvpn_dns/generated/blog_channel_ncd_list.h new file mode 100644 index 00000000..f153be78 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_list.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_list diff --git a/external/badvpn_dns/generated/blog_channel_ncd_load_module.h b/external/badvpn_dns/generated/blog_channel_ncd_load_module.h new file mode 100644 index 00000000..c27dddb8 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_load_module.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_load_module diff --git a/external/badvpn_dns/generated/blog_channel_ncd_log.h b/external/badvpn_dns/generated/blog_channel_ncd_log.h new file mode 100644 index 00000000..9ae2dc95 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_log.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_log diff --git a/external/badvpn_dns/generated/blog_channel_ncd_log_msg.h b/external/badvpn_dns/generated/blog_channel_ncd_log_msg.h new file mode 100644 index 00000000..9e51b7eb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_log_msg.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_log_msg diff --git a/external/badvpn_dns/generated/blog_channel_ncd_logical.h b/external/badvpn_dns/generated/blog_channel_ncd_logical.h new file mode 100644 index 00000000..688453d8 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_logical.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_logical diff --git a/external/badvpn_dns/generated/blog_channel_ncd_multidepend.h b/external/badvpn_dns/generated/blog_channel_ncd_multidepend.h new file mode 100644 index 00000000..a82953df --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_multidepend.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_multidepend diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_backend_badvpn.h b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_badvpn.h new file mode 100644 index 00000000..c9964c16 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_badvpn.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_badvpn diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_backend_rfkill.h b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_rfkill.h new file mode 100644 index 00000000..e69896f9 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_rfkill.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_rfkill diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitdevice.h b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitdevice.h new file mode 100644 index 00000000..63c4f242 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitdevice.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_waitdevice diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitlink.h b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitlink.h new file mode 100644 index 00000000..96244c0a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_waitlink.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_waitlink diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_backend_wpa_supplicant.h b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_wpa_supplicant.h new file mode 100644 index 00000000..22572d36 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_backend_wpa_supplicant.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_backend_wpa_supplicant diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_dns.h b/external/badvpn_dns/generated/blog_channel_ncd_net_dns.h new file mode 100644 index 00000000..01c37448 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_dns.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_dns diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_iptables.h b/external/badvpn_dns/generated/blog_channel_ncd_net_iptables.h new file mode 100644 index 00000000..42e23827 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_iptables.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_iptables diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr.h new file mode 100644 index 00000000..75bcb24d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_addr diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr_in_network.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr_in_network.h new file mode 100644 index 00000000..41f2df2e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_addr_in_network.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_addr_in_network diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_arp_probe.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_arp_probe.h new file mode 100644 index 00000000..18f7c783 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_arp_probe.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_arp_probe diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_dhcp.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_dhcp.h new file mode 100644 index 00000000..51fa61fb --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_dhcp.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_dhcp diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_route.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_route.h new file mode 100644 index 00000000..e181a90b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv4_route.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv4_route diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr.h new file mode 100644 index 00000000..bd6bd10a --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_addr diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr_in_network.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr_in_network.h new file mode 100644 index 00000000..ba33921f --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_addr_in_network.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_addr_in_network diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_route.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_route.h new file mode 100644 index 00000000..b72e4d38 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_route.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_route diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h new file mode 100644 index 00000000..ff7d6e1e --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_ipv6_wait_dynamic_addr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_up.h b/external/badvpn_dns/generated/blog_channel_ncd_net_up.h new file mode 100644 index 00000000..7acdedef --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_up.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_up diff --git a/external/badvpn_dns/generated/blog_channel_ncd_net_watch_interfaces.h b/external/badvpn_dns/generated/blog_channel_ncd_net_watch_interfaces.h new file mode 100644 index 00000000..7fc078f7 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_net_watch_interfaces.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_net_watch_interfaces diff --git a/external/badvpn_dns/generated/blog_channel_ncd_netmask.h b/external/badvpn_dns/generated/blog_channel_ncd_netmask.h new file mode 100644 index 00000000..10993f00 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_netmask.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_netmask diff --git a/external/badvpn_dns/generated/blog_channel_ncd_ondemand.h b/external/badvpn_dns/generated/blog_channel_ncd_ondemand.h new file mode 100644 index 00000000..c7a0578c --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_ondemand.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_ondemand diff --git a/external/badvpn_dns/generated/blog_channel_ncd_parse.h b/external/badvpn_dns/generated/blog_channel_ncd_parse.h new file mode 100644 index 00000000..672155be --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_parse.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_parse diff --git a/external/badvpn_dns/generated/blog_channel_ncd_print.h b/external/badvpn_dns/generated/blog_channel_ncd_print.h new file mode 100644 index 00000000..22638f3b --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_print.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_print diff --git a/external/badvpn_dns/generated/blog_channel_ncd_process_manager.h b/external/badvpn_dns/generated/blog_channel_ncd_process_manager.h new file mode 100644 index 00000000..627ba0e5 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_process_manager.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_process_manager diff --git a/external/badvpn_dns/generated/blog_channel_ncd_reboot.h b/external/badvpn_dns/generated/blog_channel_ncd_reboot.h new file mode 100644 index 00000000..0e31d555 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_reboot.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_reboot diff --git a/external/badvpn_dns/generated/blog_channel_ncd_ref.h b/external/badvpn_dns/generated/blog_channel_ncd_ref.h new file mode 100644 index 00000000..4f9f24ae --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_ref.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_ref diff --git a/external/badvpn_dns/generated/blog_channel_ncd_regex_match.h b/external/badvpn_dns/generated/blog_channel_ncd_regex_match.h new file mode 100644 index 00000000..30813479 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_regex_match.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_regex_match diff --git a/external/badvpn_dns/generated/blog_channel_ncd_request.h b/external/badvpn_dns/generated/blog_channel_ncd_request.h new file mode 100644 index 00000000..00103ea8 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_request.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_request diff --git a/external/badvpn_dns/generated/blog_channel_ncd_run.h b/external/badvpn_dns/generated/blog_channel_ncd_run.h new file mode 100644 index 00000000..036a93e3 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_run.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_run diff --git a/external/badvpn_dns/generated/blog_channel_ncd_runonce.h b/external/badvpn_dns/generated/blog_channel_ncd_runonce.h new file mode 100644 index 00000000..2e544520 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_runonce.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_runonce diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sleep.h b/external/badvpn_dns/generated/blog_channel_ncd_sleep.h new file mode 100644 index 00000000..fb6c7fe3 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sleep.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sleep diff --git a/external/badvpn_dns/generated/blog_channel_ncd_socket.h b/external/badvpn_dns/generated/blog_channel_ncd_socket.h new file mode 100644 index 00000000..3c1f0c42 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_socket.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_socket diff --git a/external/badvpn_dns/generated/blog_channel_ncd_spawn.h b/external/badvpn_dns/generated/blog_channel_ncd_spawn.h new file mode 100644 index 00000000..b9b3b24f --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_spawn.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_spawn diff --git a/external/badvpn_dns/generated/blog_channel_ncd_strcmp.h b/external/badvpn_dns/generated/blog_channel_ncd_strcmp.h new file mode 100644 index 00000000..6ef09adc --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_strcmp.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_strcmp diff --git a/external/badvpn_dns/generated/blog_channel_ncd_substr.h b/external/badvpn_dns/generated/blog_channel_ncd_substr.h new file mode 100644 index 00000000..691ad0e2 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_substr.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_substr diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_evdev.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_evdev.h new file mode 100644 index 00000000..4a7244e5 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_evdev.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_evdev diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_request_client.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_request_client.h new file mode 100644 index 00000000..ce0f9e4c --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_request_client.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_request_client diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_request_server.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_request_server.h new file mode 100644 index 00000000..11979588 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_request_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_request_server diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_start_process.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_start_process.h new file mode 100644 index 00000000..45c2edc0 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_start_process.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_start_process diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_directory.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_directory.h new file mode 100644 index 00000000..e190da51 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_directory.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_directory diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_input.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_input.h new file mode 100644 index 00000000..b8995556 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_input.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_input diff --git a/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_usb.h b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_usb.h new file mode 100644 index 00000000..bc5102a0 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_sys_watch_usb.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_sys_watch_usb diff --git a/external/badvpn_dns/generated/blog_channel_ncd_timer.h b/external/badvpn_dns/generated/blog_channel_ncd_timer.h new file mode 100644 index 00000000..beaa73db --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_timer.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_timer diff --git a/external/badvpn_dns/generated/blog_channel_ncd_to_string.h b/external/badvpn_dns/generated/blog_channel_ncd_to_string.h new file mode 100644 index 00000000..41cd8b98 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_to_string.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_to_string diff --git a/external/badvpn_dns/generated/blog_channel_ncd_try.h b/external/badvpn_dns/generated/blog_channel_ncd_try.h new file mode 100644 index 00000000..bb76c685 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_try.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_try diff --git a/external/badvpn_dns/generated/blog_channel_ncd_value.h b/external/badvpn_dns/generated/blog_channel_ncd_value.h new file mode 100644 index 00000000..fa624e8f --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_value.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_value diff --git a/external/badvpn_dns/generated/blog_channel_ncd_valuemetic.h b/external/badvpn_dns/generated/blog_channel_ncd_valuemetic.h new file mode 100644 index 00000000..385d2bb1 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_valuemetic.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_valuemetic diff --git a/external/badvpn_dns/generated/blog_channel_ncd_var.h b/external/badvpn_dns/generated/blog_channel_ncd_var.h new file mode 100644 index 00000000..fa5c0c43 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_ncd_var.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_ncd_var diff --git a/external/badvpn_dns/generated/blog_channel_nsskey.h b/external/badvpn_dns/generated/blog_channel_nsskey.h new file mode 100644 index 00000000..66e6a72d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_nsskey.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_nsskey diff --git a/external/badvpn_dns/generated/blog_channel_server.h b/external/badvpn_dns/generated/blog_channel_server.h new file mode 100644 index 00000000..acb3ed0d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_server.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_server diff --git a/external/badvpn_dns/generated/blog_channel_tun2socks.h b/external/badvpn_dns/generated/blog_channel_tun2socks.h new file mode 100644 index 00000000..21c1ce2d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_tun2socks.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_tun2socks diff --git a/external/badvpn_dns/generated/blog_channel_udpgw.h b/external/badvpn_dns/generated/blog_channel_udpgw.h new file mode 100644 index 00000000..504a3522 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channel_udpgw.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_udpgw diff --git a/external/badvpn_dns/generated/blog_channels_defines.h b/external/badvpn_dns/generated/blog_channels_defines.h new file mode 100644 index 00000000..d89dc86d --- /dev/null +++ b/external/badvpn_dns/generated/blog_channels_defines.h @@ -0,0 +1,146 @@ +#define BLOG_CHANNEL_server 0 +#define BLOG_CHANNEL_client 1 +#define BLOG_CHANNEL_flooder 2 +#define BLOG_CHANNEL_tun2socks 3 +#define BLOG_CHANNEL_ncd 4 +#define BLOG_CHANNEL_ncd_var 5 +#define BLOG_CHANNEL_ncd_list 6 +#define BLOG_CHANNEL_ncd_depend 7 +#define BLOG_CHANNEL_ncd_multidepend 8 +#define BLOG_CHANNEL_ncd_dynamic_depend 9 +#define BLOG_CHANNEL_ncd_concat 10 +#define BLOG_CHANNEL_ncd_if 11 +#define BLOG_CHANNEL_ncd_strcmp 12 +#define BLOG_CHANNEL_ncd_regex_match 13 +#define BLOG_CHANNEL_ncd_logical 14 +#define BLOG_CHANNEL_ncd_sleep 15 +#define BLOG_CHANNEL_ncd_print 16 +#define BLOG_CHANNEL_ncd_blocker 17 +#define BLOG_CHANNEL_ncd_run 18 +#define BLOG_CHANNEL_ncd_runonce 19 +#define BLOG_CHANNEL_ncd_daemon 20 +#define BLOG_CHANNEL_ncd_spawn 21 +#define BLOG_CHANNEL_ncd_imperative 22 +#define BLOG_CHANNEL_ncd_ref 23 +#define BLOG_CHANNEL_ncd_index 24 +#define BLOG_CHANNEL_ncd_alias 25 +#define BLOG_CHANNEL_ncd_process_manager 26 +#define BLOG_CHANNEL_ncd_ondemand 27 +#define BLOG_CHANNEL_ncd_foreach 28 +#define BLOG_CHANNEL_ncd_choose 29 +#define BLOG_CHANNEL_ncd_net_backend_waitdevice 30 +#define BLOG_CHANNEL_ncd_net_backend_waitlink 31 +#define BLOG_CHANNEL_ncd_net_backend_badvpn 32 +#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 33 +#define BLOG_CHANNEL_ncd_net_backend_rfkill 34 +#define BLOG_CHANNEL_ncd_net_up 35 +#define BLOG_CHANNEL_ncd_net_dns 36 +#define BLOG_CHANNEL_ncd_net_iptables 37 +#define BLOG_CHANNEL_ncd_net_ipv4_addr 38 +#define BLOG_CHANNEL_ncd_net_ipv4_route 39 +#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 40 +#define BLOG_CHANNEL_ncd_net_ipv4_arp_probe 41 +#define BLOG_CHANNEL_ncd_net_watch_interfaces 42 +#define BLOG_CHANNEL_ncd_sys_watch_input 43 +#define BLOG_CHANNEL_ncd_sys_watch_usb 44 +#define BLOG_CHANNEL_ncd_sys_evdev 45 +#define BLOG_CHANNEL_ncd_sys_watch_directory 46 +#define BLOG_CHANNEL_StreamPeerIO 47 +#define BLOG_CHANNEL_DatagramPeerIO 48 +#define BLOG_CHANNEL_BReactor 49 +#define BLOG_CHANNEL_BSignal 50 +#define BLOG_CHANNEL_FragmentProtoAssembler 51 +#define BLOG_CHANNEL_BPredicate 52 +#define BLOG_CHANNEL_ServerConnection 53 +#define BLOG_CHANNEL_Listener 54 +#define BLOG_CHANNEL_DataProto 55 +#define BLOG_CHANNEL_FrameDecider 56 +#define BLOG_CHANNEL_BSocksClient 57 +#define BLOG_CHANNEL_BDHCPClientCore 58 +#define BLOG_CHANNEL_BDHCPClient 59 +#define BLOG_CHANNEL_NCDIfConfig 60 +#define BLOG_CHANNEL_BUnixSignal 61 +#define BLOG_CHANNEL_BProcess 62 +#define BLOG_CHANNEL_PRStreamSink 63 +#define BLOG_CHANNEL_PRStreamSource 64 +#define BLOG_CHANNEL_PacketProtoDecoder 65 +#define BLOG_CHANNEL_DPRelay 66 +#define BLOG_CHANNEL_BThreadWork 67 +#define BLOG_CHANNEL_DPReceive 68 +#define BLOG_CHANNEL_BInputProcess 69 +#define BLOG_CHANNEL_NCDUdevMonitorParser 70 +#define BLOG_CHANNEL_NCDUdevMonitor 71 +#define BLOG_CHANNEL_NCDUdevCache 72 +#define BLOG_CHANNEL_NCDUdevManager 73 +#define BLOG_CHANNEL_BTime 74 +#define BLOG_CHANNEL_BEncryption 75 +#define BLOG_CHANNEL_SPProtoDecoder 76 +#define BLOG_CHANNEL_LineBuffer 77 +#define BLOG_CHANNEL_BTap 78 +#define BLOG_CHANNEL_lwip 79 +#define BLOG_CHANNEL_NCDConfigTokenizer 80 +#define BLOG_CHANNEL_NCDConfigParser 81 +#define BLOG_CHANNEL_NCDValParser 82 +#define BLOG_CHANNEL_nsskey 83 +#define BLOG_CHANNEL_addr 84 +#define BLOG_CHANNEL_PasswordListener 85 +#define BLOG_CHANNEL_NCDInterfaceMonitor 86 +#define BLOG_CHANNEL_NCDRfkillMonitor 87 +#define BLOG_CHANNEL_udpgw 88 +#define BLOG_CHANNEL_UdpGwClient 89 +#define BLOG_CHANNEL_SocksUdpGwClient 90 +#define BLOG_CHANNEL_BNetwork 91 +#define BLOG_CHANNEL_BConnection 92 +#define BLOG_CHANNEL_BSSLConnection 93 +#define BLOG_CHANNEL_BDatagram 94 +#define BLOG_CHANNEL_PeerChat 95 +#define BLOG_CHANNEL_BArpProbe 96 +#define BLOG_CHANNEL_NCDModuleIndex 97 +#define BLOG_CHANNEL_NCDModuleProcess 98 +#define BLOG_CHANNEL_NCDValGenerator 99 +#define BLOG_CHANNEL_ncd_from_string 100 +#define BLOG_CHANNEL_ncd_to_string 101 +#define BLOG_CHANNEL_ncd_value 102 +#define BLOG_CHANNEL_ncd_try 103 +#define BLOG_CHANNEL_ncd_sys_request_server 104 +#define BLOG_CHANNEL_NCDRequest 105 +#define BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr 106 +#define BLOG_CHANNEL_NCDRequestClient 107 +#define BLOG_CHANNEL_ncd_request 108 +#define BLOG_CHANNEL_ncd_sys_request_client 109 +#define BLOG_CHANNEL_ncd_exit 110 +#define BLOG_CHANNEL_ncd_getargs 111 +#define BLOG_CHANNEL_ncd_arithmetic 112 +#define BLOG_CHANNEL_ncd_parse 113 +#define BLOG_CHANNEL_ncd_valuemetic 114 +#define BLOG_CHANNEL_ncd_file 115 +#define BLOG_CHANNEL_ncd_netmask 116 +#define BLOG_CHANNEL_ncd_implode 117 +#define BLOG_CHANNEL_ncd_call2 118 +#define BLOG_CHANNEL_ncd_assert 119 +#define BLOG_CHANNEL_ncd_reboot 120 +#define BLOG_CHANNEL_ncd_explode 121 +#define BLOG_CHANNEL_NCDPlaceholderDb 122 +#define BLOG_CHANNEL_NCDVal 123 +#define BLOG_CHANNEL_ncd_net_ipv6_addr 124 +#define BLOG_CHANNEL_ncd_net_ipv6_route 125 +#define BLOG_CHANNEL_ncd_net_ipv4_addr_in_network 126 +#define BLOG_CHANNEL_ncd_net_ipv6_addr_in_network 127 +#define BLOG_CHANNEL_dostest_server 128 +#define BLOG_CHANNEL_dostest_attacker 129 +#define BLOG_CHANNEL_ncd_timer 130 +#define BLOG_CHANNEL_ncd_file_open 131 +#define BLOG_CHANNEL_ncd_backtrack 132 +#define BLOG_CHANNEL_ncd_socket 133 +#define BLOG_CHANNEL_ncd_depend_scope 134 +#define BLOG_CHANNEL_ncd_substr 135 +#define BLOG_CHANNEL_ncd_sys_start_process 136 +#define BLOG_CHANNEL_NCDBuildProgram 137 +#define BLOG_CHANNEL_ncd_log 138 +#define BLOG_CHANNEL_ncd_log_msg 139 +#define BLOG_CHANNEL_ncd_buffer 140 +#define BLOG_CHANNEL_ncd_getenv 141 +#define BLOG_CHANNEL_BThreadSignal 142 +#define BLOG_CHANNEL_BLockReactor 143 +#define BLOG_CHANNEL_ncd_load_module 144 +#define BLOG_NUM_CHANNELS 145 diff --git a/external/badvpn_dns/generated/blog_channels_list.h b/external/badvpn_dns/generated/blog_channels_list.h new file mode 100644 index 00000000..c903c0f6 --- /dev/null +++ b/external/badvpn_dns/generated/blog_channels_list.h @@ -0,0 +1,145 @@ +{"server", 4}, +{"client", 4}, +{"flooder", 4}, +{"tun2socks", 4}, +{"ncd", 4}, +{"ncd_var", 4}, +{"ncd_list", 4}, +{"ncd_depend", 4}, +{"ncd_multidepend", 4}, +{"ncd_dynamic_depend", 4}, +{"ncd_concat", 4}, +{"ncd_if", 4}, +{"ncd_strcmp", 4}, +{"ncd_regex_match", 4}, +{"ncd_logical", 4}, +{"ncd_sleep", 4}, +{"ncd_print", 4}, +{"ncd_blocker", 4}, +{"ncd_run", 4}, +{"ncd_runonce", 4}, +{"ncd_daemon", 4}, +{"ncd_spawn", 4}, +{"ncd_imperative", 4}, +{"ncd_ref", 4}, +{"ncd_index", 4}, +{"ncd_alias", 4}, +{"ncd_process_manager", 4}, +{"ncd_ondemand", 4}, +{"ncd_foreach", 4}, +{"ncd_choose", 4}, +{"ncd_net_backend_waitdevice", 4}, +{"ncd_net_backend_waitlink", 4}, +{"ncd_net_backend_badvpn", 4}, +{"ncd_net_backend_wpa_supplicant", 4}, +{"ncd_net_backend_rfkill", 4}, +{"ncd_net_up", 4}, +{"ncd_net_dns", 4}, +{"ncd_net_iptables", 4}, +{"ncd_net_ipv4_addr", 4}, +{"ncd_net_ipv4_route", 4}, +{"ncd_net_ipv4_dhcp", 4}, +{"ncd_net_ipv4_arp_probe", 4}, +{"ncd_net_watch_interfaces", 4}, +{"ncd_sys_watch_input", 4}, +{"ncd_sys_watch_usb", 4}, +{"ncd_sys_evdev", 4}, +{"ncd_sys_watch_directory", 4}, +{"StreamPeerIO", 4}, +{"DatagramPeerIO", 4}, +{"BReactor", 3}, +{"BSignal", 3}, +{"FragmentProtoAssembler", 4}, +{"BPredicate", 3}, +{"ServerConnection", 4}, +{"Listener", 4}, +{"DataProto", 4}, +{"FrameDecider", 4}, +{"BSocksClient", 4}, +{"BDHCPClientCore", 4}, +{"BDHCPClient", 4}, +{"NCDIfConfig", 4}, +{"BUnixSignal", 4}, +{"BProcess", 4}, +{"PRStreamSink", 4}, +{"PRStreamSource", 4}, +{"PacketProtoDecoder", 4}, +{"DPRelay", 4}, +{"BThreadWork", 4}, +{"DPReceive", 4}, +{"BInputProcess", 4}, +{"NCDUdevMonitorParser", 4}, +{"NCDUdevMonitor", 4}, +{"NCDUdevCache", 4}, +{"NCDUdevManager", 4}, +{"BTime", 4}, +{"BEncryption", 4}, +{"SPProtoDecoder", 4}, +{"LineBuffer", 4}, +{"BTap", 4}, +{"lwip", 4}, +{"NCDConfigTokenizer", 4}, +{"NCDConfigParser", 4}, +{"NCDValParser", 4}, +{"nsskey", 4}, +{"addr", 4}, +{"PasswordListener", 4}, +{"NCDInterfaceMonitor", 4}, +{"NCDRfkillMonitor", 4}, +{"udpgw", 4}, +{"UdpGwClient", 4}, +{"SocksUdpGwClient", 4}, +{"BNetwork", 4}, +{"BConnection", 4}, +{"BSSLConnection", 4}, +{"BDatagram", 4}, +{"PeerChat", 4}, +{"BArpProbe", 4}, +{"NCDModuleIndex", 4}, +{"NCDModuleProcess", 4}, +{"NCDValGenerator", 4}, +{"ncd_from_string", 4}, +{"ncd_to_string", 4}, +{"ncd_value", 4}, +{"ncd_try", 4}, +{"ncd_sys_request_server", 4}, +{"NCDRequest", 4}, +{"ncd_net_ipv6_wait_dynamic_addr", 4}, +{"NCDRequestClient", 4}, +{"ncd_request", 4}, +{"ncd_sys_request_client", 4}, +{"ncd_exit", 4}, +{"ncd_getargs", 4}, +{"ncd_arithmetic", 4}, +{"ncd_parse", 4}, +{"ncd_valuemetic", 4}, +{"ncd_file", 4}, +{"ncd_netmask", 4}, +{"ncd_implode", 4}, +{"ncd_call2", 4}, +{"ncd_assert", 4}, +{"ncd_reboot", 4}, +{"ncd_explode", 4}, +{"NCDPlaceholderDb", 4}, +{"NCDVal", 4}, +{"ncd_net_ipv6_addr", 4}, +{"ncd_net_ipv6_route", 4}, +{"ncd_net_ipv4_addr_in_network", 4}, +{"ncd_net_ipv6_addr_in_network", 4}, +{"dostest_server", 4}, +{"dostest_attacker", 4}, +{"ncd_timer", 4}, +{"ncd_file_open", 4}, +{"ncd_backtrack", 4}, +{"ncd_socket", 4}, +{"ncd_depend_scope", 4}, +{"ncd_substr", 4}, +{"ncd_sys_start_process", 4}, +{"NCDBuildProgram", 4}, +{"ncd_log", 4}, +{"ncd_log_msg", 4}, +{"ncd_buffer", 4}, +{"ncd_getenv", 4}, +{"BThreadSignal", 4}, +{"BLockReactor", 4}, +{"ncd_load_module", 4}, diff --git a/external/badvpn_dns/generated/bproto_addr.h b/external/badvpn_dns/generated/bproto_addr.h new file mode 100644 index 00000000..fbd96a80 --- /dev/null +++ b/external/badvpn_dns/generated/bproto_addr.h @@ -0,0 +1,675 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define addr_SIZEtype (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint8_s)) +#define addr_SIZEip_port (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (2)) +#define addr_SIZEipv4_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (4)) +#define addr_SIZEipv6_addr (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (16)) + +typedef struct { + uint8_t *out; + int used; + int type_count; + int ip_port_count; + int ipv4_addr_count; + int ipv6_addr_count; +} addrWriter; + +static void addrWriter_Init (addrWriter *o, uint8_t *out); +static int addrWriter_Finish (addrWriter *o); +static void addrWriter_Addtype (addrWriter *o, uint8_t v); +static uint8_t * addrWriter_Addip_port (addrWriter *o); +static uint8_t * addrWriter_Addipv4_addr (addrWriter *o); +static uint8_t * addrWriter_Addipv6_addr (addrWriter *o); + +typedef struct { + uint8_t *buf; + int buf_len; + int type_start; + int type_span; + int type_pos; + int ip_port_start; + int ip_port_span; + int ip_port_pos; + int ipv4_addr_start; + int ipv4_addr_span; + int ipv4_addr_pos; + int ipv6_addr_start; + int ipv6_addr_span; + int ipv6_addr_pos; +} addrParser; + +static int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len); +static int addrParser_GotEverything (addrParser *o); +static int addrParser_Gettype (addrParser *o, uint8_t *v); +static void addrParser_Resettype (addrParser *o); +static void addrParser_Forwardtype (addrParser *o); +static int addrParser_Getip_port (addrParser *o, uint8_t **data); +static void addrParser_Resetip_port (addrParser *o); +static void addrParser_Forwardip_port (addrParser *o); +static int addrParser_Getipv4_addr (addrParser *o, uint8_t **data); +static void addrParser_Resetipv4_addr (addrParser *o); +static void addrParser_Forwardipv4_addr (addrParser *o); +static int addrParser_Getipv6_addr (addrParser *o, uint8_t **data); +static void addrParser_Resetipv6_addr (addrParser *o); +static void addrParser_Forwardipv6_addr (addrParser *o); + +void addrWriter_Init (addrWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->type_count = 0; + o->ip_port_count = 0; + o->ipv4_addr_count = 0; + o->ipv6_addr_count = 0; +} + +int addrWriter_Finish (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 1) + ASSERT(o->ip_port_count >= 0 && o->ip_port_count <= 1) + ASSERT(o->ipv4_addr_count >= 0 && o->ipv4_addr_count <= 1) + ASSERT(o->ipv6_addr_count >= 0 && o->ipv6_addr_count <= 1) + + return o->used; +} + +void addrWriter_Addtype (addrWriter *o, uint8_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT8); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint8_s data; + data.v = htol8(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint8_s); + + o->type_count++; +} + +uint8_t * addrWriter_Addip_port (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ip_port_count == 0) + + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(2); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (2); + + o->ip_port_count++; + + return dest; +} + +uint8_t * addrWriter_Addipv4_addr (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ipv4_addr_count == 0) + + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(4); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (4); + + o->ipv4_addr_count++; + + return dest; +} + +uint8_t * addrWriter_Addipv6_addr (addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->ipv6_addr_count == 0) + + + struct BProto_header_s header; + header.id = htol16(4); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(16); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (16); + + o->ipv6_addr_count++; + + return dest; +} + +int addrParser_Init (addrParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->type_start = o->buf_len; + o->type_span = 0; + o->type_pos = 0; + o->ip_port_start = o->buf_len; + o->ip_port_span = 0; + o->ip_port_pos = 0; + o->ipv4_addr_start = o->buf_len; + o->ipv4_addr_span = 0; + o->ipv4_addr_pos = 0; + o->ipv6_addr_start = o->buf_len; + o->ipv6_addr_span = 0; + o->ipv6_addr_pos = 0; + + int type_count = 0; + int ip_port_count = 0; + int ipv4_addr_count = 0; + int ipv6_addr_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + case 1: + if (o->type_start == o->buf_len) { + o->type_start = entry_pos; + } + o->type_span = pos - o->type_start; + type_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (2))) { + return 0; + } + if (o->ip_port_start == o->buf_len) { + o->ip_port_start = entry_pos; + } + o->ip_port_span = pos - o->ip_port_start; + ip_port_count++; + break; + case 3: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (4))) { + return 0; + } + if (o->ipv4_addr_start == o->buf_len) { + o->ipv4_addr_start = entry_pos; + } + o->ipv4_addr_span = pos - o->ipv4_addr_start; + ipv4_addr_count++; + break; + case 4: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (16))) { + return 0; + } + if (o->ipv6_addr_start == o->buf_len) { + o->ipv6_addr_start = entry_pos; + } + o->ipv6_addr_span = pos - o->ipv6_addr_start; + ipv6_addr_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(type_count == 1)) { + return 0; + } + if (!(ip_port_count <= 1)) { + return 0; + } + if (!(ipv4_addr_count <= 1)) { + return 0; + } + if (!(ipv6_addr_count <= 1)) { + return 0; + } + + return 1; +} + +int addrParser_GotEverything (addrParser *o) +{ + return ( + o->type_pos == o->type_span + && + o->ip_port_pos == o->ip_port_span + && + o->ipv4_addr_pos == o->ipv4_addr_span + && + o->ipv6_addr_pos == o->ipv6_addr_span + ); +} + +int addrParser_Gettype (addrParser *o, uint8_t *v) +{ + ASSERT(o->type_pos >= 0) + ASSERT(o->type_pos <= o->type_span) + + int left = o->type_span - o->type_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->type_start + o->type_pos, sizeof(header)); + o->type_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + struct BProto_uint8_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + if (id == 1) { + *v = ltoh8(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->type_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->type_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->type_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->type_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resettype (addrParser *o) +{ + o->type_pos = 0; +} + +void addrParser_Forwardtype (addrParser *o) +{ + o->type_pos = o->type_span; +} + +int addrParser_Getip_port (addrParser *o, uint8_t **data) +{ + ASSERT(o->ip_port_pos >= 0) + ASSERT(o->ip_port_pos <= o->ip_port_span) + + int left = o->ip_port_span - o->ip_port_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(header)); + o->ip_port_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ip_port_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ip_port_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ip_port_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ip_port_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ip_port_start + o->ip_port_pos, sizeof(val)); + o->ip_port_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ip_port_start + o->ip_port_pos; + o->ip_port_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 2) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetip_port (addrParser *o) +{ + o->ip_port_pos = 0; +} + +void addrParser_Forwardip_port (addrParser *o) +{ + o->ip_port_pos = o->ip_port_span; +} + +int addrParser_Getipv4_addr (addrParser *o, uint8_t **data) +{ + ASSERT(o->ipv4_addr_pos >= 0) + ASSERT(o->ipv4_addr_pos <= o->ipv4_addr_span) + + int left = o->ipv4_addr_span - o->ipv4_addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(header)); + o->ipv4_addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ipv4_addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ipv4_addr_start + o->ipv4_addr_pos, sizeof(val)); + o->ipv4_addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ipv4_addr_start + o->ipv4_addr_pos; + o->ipv4_addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 3) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetipv4_addr (addrParser *o) +{ + o->ipv4_addr_pos = 0; +} + +void addrParser_Forwardipv4_addr (addrParser *o) +{ + o->ipv4_addr_pos = o->ipv4_addr_span; +} + +int addrParser_Getipv6_addr (addrParser *o, uint8_t **data) +{ + ASSERT(o->ipv6_addr_pos >= 0) + ASSERT(o->ipv6_addr_pos <= o->ipv6_addr_span) + + int left = o->ipv6_addr_span - o->ipv6_addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(header)); + o->ipv6_addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->ipv6_addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->ipv6_addr_start + o->ipv6_addr_pos, sizeof(val)); + o->ipv6_addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->ipv6_addr_start + o->ipv6_addr_pos; + o->ipv6_addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 4) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void addrParser_Resetipv6_addr (addrParser *o) +{ + o->ipv6_addr_pos = 0; +} + +void addrParser_Forwardipv6_addr (addrParser *o) +{ + o->ipv6_addr_pos = o->ipv6_addr_span; +} + diff --git a/external/badvpn_dns/generated/bproto_bproto_test.h b/external/badvpn_dns/generated/bproto_bproto_test.h new file mode 100644 index 00000000..dc4ad804 --- /dev/null +++ b/external/badvpn_dns/generated/bproto_bproto_test.h @@ -0,0 +1,1029 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define msg1_SIZEa (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg1_SIZEb (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint32_s)) +#define msg1_SIZEc (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint64_s)) +#define msg1_SIZEd (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg1_SIZEe (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint8_s)) +#define msg1_SIZEf(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg1_SIZEg (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (4)) + +typedef struct { + uint8_t *out; + int used; + int a_count; + int b_count; + int c_count; + int d_count; + int e_count; + int f_count; + int g_count; +} msg1Writer; + +static void msg1Writer_Init (msg1Writer *o, uint8_t *out); +static int msg1Writer_Finish (msg1Writer *o); +static void msg1Writer_Adda (msg1Writer *o, uint16_t v); +static void msg1Writer_Addb (msg1Writer *o, uint32_t v); +static void msg1Writer_Addc (msg1Writer *o, uint64_t v); +static void msg1Writer_Addd (msg1Writer *o, uint16_t v); +static void msg1Writer_Adde (msg1Writer *o, uint8_t v); +static uint8_t * msg1Writer_Addf (msg1Writer *o, int len); +static uint8_t * msg1Writer_Addg (msg1Writer *o); + +typedef struct { + uint8_t *buf; + int buf_len; + int a_start; + int a_span; + int a_pos; + int b_start; + int b_span; + int b_pos; + int c_start; + int c_span; + int c_pos; + int d_start; + int d_span; + int d_pos; + int e_start; + int e_span; + int e_pos; + int f_start; + int f_span; + int f_pos; + int g_start; + int g_span; + int g_pos; +} msg1Parser; + +static int msg1Parser_Init (msg1Parser *o, uint8_t *buf, int buf_len); +static int msg1Parser_GotEverything (msg1Parser *o); +static int msg1Parser_Geta (msg1Parser *o, uint16_t *v); +static void msg1Parser_Reseta (msg1Parser *o); +static void msg1Parser_Forwarda (msg1Parser *o); +static int msg1Parser_Getb (msg1Parser *o, uint32_t *v); +static void msg1Parser_Resetb (msg1Parser *o); +static void msg1Parser_Forwardb (msg1Parser *o); +static int msg1Parser_Getc (msg1Parser *o, uint64_t *v); +static void msg1Parser_Resetc (msg1Parser *o); +static void msg1Parser_Forwardc (msg1Parser *o); +static int msg1Parser_Getd (msg1Parser *o, uint16_t *v); +static void msg1Parser_Resetd (msg1Parser *o); +static void msg1Parser_Forwardd (msg1Parser *o); +static int msg1Parser_Gete (msg1Parser *o, uint8_t *v); +static void msg1Parser_Resete (msg1Parser *o); +static void msg1Parser_Forwarde (msg1Parser *o); +static int msg1Parser_Getf (msg1Parser *o, uint8_t **data, int *data_len); +static void msg1Parser_Resetf (msg1Parser *o); +static void msg1Parser_Forwardf (msg1Parser *o); +static int msg1Parser_Getg (msg1Parser *o, uint8_t **data); +static void msg1Parser_Resetg (msg1Parser *o); +static void msg1Parser_Forwardg (msg1Parser *o); + +void msg1Writer_Init (msg1Writer *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->a_count = 0; + o->b_count = 0; + o->c_count = 0; + o->d_count = 0; + o->e_count = 0; + o->f_count = 0; + o->g_count = 0; +} + +int msg1Writer_Finish (msg1Writer *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->a_count == 1) + ASSERT(o->b_count >= 0 && o->b_count <= 1) + ASSERT(o->c_count >= 1) + ASSERT(o->d_count >= 0) + ASSERT(o->e_count == 1) + ASSERT(o->f_count == 1) + ASSERT(o->g_count == 1) + + return o->used; +} + +void msg1Writer_Adda (msg1Writer *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->a_count == 0) + + + struct BProto_header_s header; + header.id = htol16(5); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->a_count++; +} + +void msg1Writer_Addb (msg1Writer *o, uint32_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->b_count == 0) + + + struct BProto_header_s header; + header.id = htol16(6); + header.type = htol16(BPROTO_TYPE_UINT32); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint32_s data; + data.v = htol32(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint32_s); + + o->b_count++; +} + +void msg1Writer_Addc (msg1Writer *o, uint64_t v) +{ + ASSERT(o->used >= 0) + + + + struct BProto_header_s header; + header.id = htol16(7); + header.type = htol16(BPROTO_TYPE_UINT64); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint64_s data; + data.v = htol64(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint64_s); + + o->c_count++; +} + +void msg1Writer_Addd (msg1Writer *o, uint16_t v) +{ + ASSERT(o->used >= 0) + + + + struct BProto_header_s header; + header.id = htol16(8); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->d_count++; +} + +void msg1Writer_Adde (msg1Writer *o, uint8_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->e_count == 0) + + + struct BProto_header_s header; + header.id = htol16(9); + header.type = htol16(BPROTO_TYPE_UINT8); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint8_s data; + data.v = htol8(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint8_s); + + o->e_count++; +} + +uint8_t * msg1Writer_Addf (msg1Writer *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->f_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(10); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->f_count++; + + return dest; +} + +uint8_t * msg1Writer_Addg (msg1Writer *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->g_count == 0) + + + struct BProto_header_s header; + header.id = htol16(11); + header.type = htol16(BPROTO_TYPE_CONSTDATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(4); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += (4); + + o->g_count++; + + return dest; +} + +int msg1Parser_Init (msg1Parser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->a_start = o->buf_len; + o->a_span = 0; + o->a_pos = 0; + o->b_start = o->buf_len; + o->b_span = 0; + o->b_pos = 0; + o->c_start = o->buf_len; + o->c_span = 0; + o->c_pos = 0; + o->d_start = o->buf_len; + o->d_span = 0; + o->d_pos = 0; + o->e_start = o->buf_len; + o->e_span = 0; + o->e_pos = 0; + o->f_start = o->buf_len; + o->f_span = 0; + o->f_pos = 0; + o->g_start = o->buf_len; + o->g_span = 0; + o->g_pos = 0; + + int a_count = 0; + int b_count = 0; + int c_count = 0; + int d_count = 0; + int e_count = 0; + int f_count = 0; + int g_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + case 9: + if (o->e_start == o->buf_len) { + o->e_start = entry_pos; + } + o->e_span = pos - o->e_start; + e_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 5: + if (o->a_start == o->buf_len) { + o->a_start = entry_pos; + } + o->a_span = pos - o->a_start; + a_count++; + break; + case 8: + if (o->d_start == o->buf_len) { + o->d_start = entry_pos; + } + o->d_span = pos - o->d_start; + d_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + case 6: + if (o->b_start == o->buf_len) { + o->b_start = entry_pos; + } + o->b_span = pos - o->b_start; + b_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + case 7: + if (o->c_start == o->buf_len) { + o->c_start = entry_pos; + } + o->c_span = pos - o->c_start; + c_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 10: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->f_start == o->buf_len) { + o->f_start = entry_pos; + } + o->f_span = pos - o->f_start; + f_count++; + break; + case 11: + if (!(type == BPROTO_TYPE_CONSTDATA)) { + return 0; + } + if (!(payload_len == (4))) { + return 0; + } + if (o->g_start == o->buf_len) { + o->g_start = entry_pos; + } + o->g_span = pos - o->g_start; + g_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(a_count == 1)) { + return 0; + } + if (!(b_count <= 1)) { + return 0; + } + if (!(c_count >= 1)) { + return 0; + } + if (!(e_count == 1)) { + return 0; + } + if (!(f_count == 1)) { + return 0; + } + if (!(g_count == 1)) { + return 0; + } + + return 1; +} + +int msg1Parser_GotEverything (msg1Parser *o) +{ + return ( + o->a_pos == o->a_span + && + o->b_pos == o->b_span + && + o->c_pos == o->c_span + && + o->d_pos == o->d_span + && + o->e_pos == o->e_span + && + o->f_pos == o->f_span + && + o->g_pos == o->g_span + ); +} + +int msg1Parser_Geta (msg1Parser *o, uint16_t *v) +{ + ASSERT(o->a_pos >= 0) + ASSERT(o->a_pos <= o->a_span) + + int left = o->a_span - o->a_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->a_start + o->a_pos, sizeof(header)); + o->a_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->a_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->a_start + o->a_pos, sizeof(val)); + o->a_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 5) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->a_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->a_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->a_start + o->a_pos, sizeof(val)); + o->a_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->a_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Reseta (msg1Parser *o) +{ + o->a_pos = 0; +} + +void msg1Parser_Forwarda (msg1Parser *o) +{ + o->a_pos = o->a_span; +} + +int msg1Parser_Getb (msg1Parser *o, uint32_t *v) +{ + ASSERT(o->b_pos >= 0) + ASSERT(o->b_pos <= o->b_span) + + int left = o->b_span - o->b_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->b_start + o->b_pos, sizeof(header)); + o->b_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->b_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->b_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + struct BProto_uint32_s val; + memcpy(&val, o->buf + o->b_start + o->b_pos, sizeof(val)); + o->b_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + if (id == 6) { + *v = ltoh32(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->b_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->b_start + o->b_pos, sizeof(val)); + o->b_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->b_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetb (msg1Parser *o) +{ + o->b_pos = 0; +} + +void msg1Parser_Forwardb (msg1Parser *o) +{ + o->b_pos = o->b_span; +} + +int msg1Parser_Getc (msg1Parser *o, uint64_t *v) +{ + ASSERT(o->c_pos >= 0) + ASSERT(o->c_pos <= o->c_span) + + int left = o->c_span - o->c_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->c_start + o->c_pos, sizeof(header)); + o->c_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->c_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->c_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->c_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + struct BProto_uint64_s val; + memcpy(&val, o->buf + o->c_start + o->c_pos, sizeof(val)); + o->c_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + if (id == 7) { + *v = ltoh64(val.v); + return 1; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->c_start + o->c_pos, sizeof(val)); + o->c_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->c_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetc (msg1Parser *o) +{ + o->c_pos = 0; +} + +void msg1Parser_Forwardc (msg1Parser *o) +{ + o->c_pos = o->c_span; +} + +int msg1Parser_Getd (msg1Parser *o, uint16_t *v) +{ + ASSERT(o->d_pos >= 0) + ASSERT(o->d_pos <= o->d_span) + + int left = o->d_span - o->d_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->d_start + o->d_pos, sizeof(header)); + o->d_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->d_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->d_start + o->d_pos, sizeof(val)); + o->d_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 8) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->d_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->d_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->d_start + o->d_pos, sizeof(val)); + o->d_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->d_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetd (msg1Parser *o) +{ + o->d_pos = 0; +} + +void msg1Parser_Forwardd (msg1Parser *o) +{ + o->d_pos = o->d_span; +} + +int msg1Parser_Gete (msg1Parser *o, uint8_t *v) +{ + ASSERT(o->e_pos >= 0) + ASSERT(o->e_pos <= o->e_span) + + int left = o->e_span - o->e_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->e_start + o->e_pos, sizeof(header)); + o->e_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + struct BProto_uint8_s val; + memcpy(&val, o->buf + o->e_start + o->e_pos, sizeof(val)); + o->e_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + if (id == 9) { + *v = ltoh8(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->e_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->e_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->e_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->e_start + o->e_pos, sizeof(val)); + o->e_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->e_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resete (msg1Parser *o) +{ + o->e_pos = 0; +} + +void msg1Parser_Forwarde (msg1Parser *o) +{ + o->e_pos = o->e_span; +} + +int msg1Parser_Getf (msg1Parser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->f_pos >= 0) + ASSERT(o->f_pos <= o->f_span) + + int left = o->f_span - o->f_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->f_start + o->f_pos, sizeof(header)); + o->f_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->f_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->f_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->f_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->f_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->f_start + o->f_pos, sizeof(val)); + o->f_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->f_start + o->f_pos; + o->f_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 10) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetf (msg1Parser *o) +{ + o->f_pos = 0; +} + +void msg1Parser_Forwardf (msg1Parser *o) +{ + o->f_pos = o->f_span; +} + +int msg1Parser_Getg (msg1Parser *o, uint8_t **data) +{ + ASSERT(o->g_pos >= 0) + ASSERT(o->g_pos <= o->g_span) + + int left = o->g_span - o->g_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->g_start + o->g_pos, sizeof(header)); + o->g_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->g_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->g_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->g_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->g_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->g_start + o->g_pos, sizeof(val)); + o->g_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->g_start + o->g_pos; + o->g_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_CONSTDATA && id == 11) { + *data = payload; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg1Parser_Resetg (msg1Parser *o) +{ + o->g_pos = 0; +} + +void msg1Parser_Forwardg (msg1Parser *o) +{ + o->g_pos = o->g_span; +} + diff --git a/external/badvpn_dns/generated/bproto_msgproto.h b/external/badvpn_dns/generated/bproto_msgproto.h new file mode 100644 index 00000000..e429223d --- /dev/null +++ b/external/badvpn_dns/generated/bproto_msgproto.h @@ -0,0 +1,2122 @@ +/* + DO NOT EDIT THIS FILE! + This file was automatically generated by the bproto generator. +*/ + +#include +#include + +#include +#include +#include + + +#define msg_SIZEtype (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg_SIZEpayload(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int type_count; + int payload_count; +} msgWriter; + +static void msgWriter_Init (msgWriter *o, uint8_t *out); +static int msgWriter_Finish (msgWriter *o); +static void msgWriter_Addtype (msgWriter *o, uint16_t v); +static uint8_t * msgWriter_Addpayload (msgWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int type_start; + int type_span; + int type_pos; + int payload_start; + int payload_span; + int payload_pos; +} msgParser; + +static int msgParser_Init (msgParser *o, uint8_t *buf, int buf_len); +static int msgParser_GotEverything (msgParser *o); +static int msgParser_Gettype (msgParser *o, uint16_t *v); +static void msgParser_Resettype (msgParser *o); +static void msgParser_Forwardtype (msgParser *o); +static int msgParser_Getpayload (msgParser *o, uint8_t **data, int *data_len); +static void msgParser_Resetpayload (msgParser *o); +static void msgParser_Forwardpayload (msgParser *o); + +void msgWriter_Init (msgWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->type_count = 0; + o->payload_count = 0; +} + +int msgWriter_Finish (msgWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 1) + ASSERT(o->payload_count == 1) + + return o->used; +} + +void msgWriter_Addtype (msgWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->type_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->type_count++; +} + +uint8_t * msgWriter_Addpayload (msgWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->payload_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->payload_count++; + + return dest; +} + +int msgParser_Init (msgParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->type_start = o->buf_len; + o->type_span = 0; + o->type_pos = 0; + o->payload_start = o->buf_len; + o->payload_span = 0; + o->payload_pos = 0; + + int type_count = 0; + int payload_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->type_start == o->buf_len) { + o->type_start = entry_pos; + } + o->type_span = pos - o->type_start; + type_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->payload_start == o->buf_len) { + o->payload_start = entry_pos; + } + o->payload_span = pos - o->payload_start; + payload_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(type_count == 1)) { + return 0; + } + if (!(payload_count == 1)) { + return 0; + } + + return 1; +} + +int msgParser_GotEverything (msgParser *o) +{ + return ( + o->type_pos == o->type_span + && + o->payload_pos == o->payload_span + ); +} + +int msgParser_Gettype (msgParser *o, uint16_t *v) +{ + ASSERT(o->type_pos >= 0) + ASSERT(o->type_pos <= o->type_span) + + int left = o->type_span - o->type_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->type_start + o->type_pos, sizeof(header)); + o->type_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->type_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->type_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->type_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->type_start + o->type_pos, sizeof(val)); + o->type_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->type_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msgParser_Resettype (msgParser *o) +{ + o->type_pos = 0; +} + +void msgParser_Forwardtype (msgParser *o) +{ + o->type_pos = o->type_span; +} + +int msgParser_Getpayload (msgParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->payload_pos >= 0) + ASSERT(o->payload_pos <= o->payload_span) + + int left = o->payload_span - o->payload_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->payload_start + o->payload_pos, sizeof(header)); + o->payload_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->payload_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->payload_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->payload_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->payload_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->payload_start + o->payload_pos, sizeof(val)); + o->payload_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->payload_start + o->payload_pos; + o->payload_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msgParser_Resetpayload (msgParser *o) +{ + o->payload_pos = 0; +} + +void msgParser_Forwardpayload (msgParser *o) +{ + o->payload_pos = o->payload_span; +} + +#define msg_youconnect_SIZEaddr(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_SIZEkey(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_SIZEpassword (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint64_s)) + +typedef struct { + uint8_t *out; + int used; + int addr_count; + int key_count; + int password_count; +} msg_youconnectWriter; + +static void msg_youconnectWriter_Init (msg_youconnectWriter *o, uint8_t *out); +static int msg_youconnectWriter_Finish (msg_youconnectWriter *o); +static uint8_t * msg_youconnectWriter_Addaddr (msg_youconnectWriter *o, int len); +static uint8_t * msg_youconnectWriter_Addkey (msg_youconnectWriter *o, int len); +static void msg_youconnectWriter_Addpassword (msg_youconnectWriter *o, uint64_t v); + +typedef struct { + uint8_t *buf; + int buf_len; + int addr_start; + int addr_span; + int addr_pos; + int key_start; + int key_span; + int key_pos; + int password_start; + int password_span; + int password_pos; +} msg_youconnectParser; + +static int msg_youconnectParser_Init (msg_youconnectParser *o, uint8_t *buf, int buf_len); +static int msg_youconnectParser_GotEverything (msg_youconnectParser *o); +static int msg_youconnectParser_Getaddr (msg_youconnectParser *o, uint8_t **data, int *data_len); +static void msg_youconnectParser_Resetaddr (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardaddr (msg_youconnectParser *o); +static int msg_youconnectParser_Getkey (msg_youconnectParser *o, uint8_t **data, int *data_len); +static void msg_youconnectParser_Resetkey (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardkey (msg_youconnectParser *o); +static int msg_youconnectParser_Getpassword (msg_youconnectParser *o, uint64_t *v); +static void msg_youconnectParser_Resetpassword (msg_youconnectParser *o); +static void msg_youconnectParser_Forwardpassword (msg_youconnectParser *o); + +void msg_youconnectWriter_Init (msg_youconnectWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->addr_count = 0; + o->key_count = 0; + o->password_count = 0; +} + +int msg_youconnectWriter_Finish (msg_youconnectWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->addr_count >= 1) + ASSERT(o->key_count >= 0 && o->key_count <= 1) + ASSERT(o->password_count >= 0 && o->password_count <= 1) + + return o->used; +} + +uint8_t * msg_youconnectWriter_Addaddr (msg_youconnectWriter *o, int len) +{ + ASSERT(o->used >= 0) + + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->addr_count++; + + return dest; +} + +uint8_t * msg_youconnectWriter_Addkey (msg_youconnectWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->key_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->key_count++; + + return dest; +} + +void msg_youconnectWriter_Addpassword (msg_youconnectWriter *o, uint64_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->password_count == 0) + + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_UINT64); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint64_s data; + data.v = htol64(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint64_s); + + o->password_count++; +} + +int msg_youconnectParser_Init (msg_youconnectParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->addr_start = o->buf_len; + o->addr_span = 0; + o->addr_pos = 0; + o->key_start = o->buf_len; + o->key_span = 0; + o->key_pos = 0; + o->password_start = o->buf_len; + o->password_span = 0; + o->password_pos = 0; + + int addr_count = 0; + int key_count = 0; + int password_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + case 3: + if (o->password_start == o->buf_len) { + o->password_start = entry_pos; + } + o->password_span = pos - o->password_start; + password_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 1: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->addr_start == o->buf_len) { + o->addr_start = entry_pos; + } + o->addr_span = pos - o->addr_start; + addr_count++; + break; + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->key_start == o->buf_len) { + o->key_start = entry_pos; + } + o->key_span = pos - o->key_start; + key_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(addr_count >= 1)) { + return 0; + } + if (!(key_count <= 1)) { + return 0; + } + if (!(password_count <= 1)) { + return 0; + } + + return 1; +} + +int msg_youconnectParser_GotEverything (msg_youconnectParser *o) +{ + return ( + o->addr_pos == o->addr_span + && + o->key_pos == o->key_span + && + o->password_pos == o->password_span + ); +} + +int msg_youconnectParser_Getaddr (msg_youconnectParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->addr_pos >= 0) + ASSERT(o->addr_pos <= o->addr_span) + + int left = o->addr_span - o->addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->addr_start + o->addr_pos, sizeof(header)); + o->addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->addr_start + o->addr_pos, sizeof(val)); + o->addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->addr_start + o->addr_pos; + o->addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 1) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetaddr (msg_youconnectParser *o) +{ + o->addr_pos = 0; +} + +void msg_youconnectParser_Forwardaddr (msg_youconnectParser *o) +{ + o->addr_pos = o->addr_span; +} + +int msg_youconnectParser_Getkey (msg_youconnectParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->key_pos >= 0) + ASSERT(o->key_pos <= o->key_span) + + int left = o->key_span - o->key_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->key_start + o->key_pos, sizeof(header)); + o->key_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->key_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->key_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->key_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->key_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->key_start + o->key_pos, sizeof(val)); + o->key_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->key_start + o->key_pos; + o->key_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetkey (msg_youconnectParser *o) +{ + o->key_pos = 0; +} + +void msg_youconnectParser_Forwardkey (msg_youconnectParser *o) +{ + o->key_pos = o->key_span; +} + +int msg_youconnectParser_Getpassword (msg_youconnectParser *o, uint64_t *v) +{ + ASSERT(o->password_pos >= 0) + ASSERT(o->password_pos <= o->password_span) + + int left = o->password_span - o->password_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->password_start + o->password_pos, sizeof(header)); + o->password_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->password_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->password_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->password_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + struct BProto_uint64_s val; + memcpy(&val, o->buf + o->password_start + o->password_pos, sizeof(val)); + o->password_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + if (id == 3) { + *v = ltoh64(val.v); + return 1; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->password_start + o->password_pos, sizeof(val)); + o->password_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->password_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnectParser_Resetpassword (msg_youconnectParser *o) +{ + o->password_pos = 0; +} + +void msg_youconnectParser_Forwardpassword (msg_youconnectParser *o) +{ + o->password_pos = o->password_span; +} + +#define msg_youconnect_addr_SIZEname(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_youconnect_addr_SIZEaddr(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int name_count; + int addr_count; +} msg_youconnect_addrWriter; + +static void msg_youconnect_addrWriter_Init (msg_youconnect_addrWriter *o, uint8_t *out); +static int msg_youconnect_addrWriter_Finish (msg_youconnect_addrWriter *o); +static uint8_t * msg_youconnect_addrWriter_Addname (msg_youconnect_addrWriter *o, int len); +static uint8_t * msg_youconnect_addrWriter_Addaddr (msg_youconnect_addrWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int name_start; + int name_span; + int name_pos; + int addr_start; + int addr_span; + int addr_pos; +} msg_youconnect_addrParser; + +static int msg_youconnect_addrParser_Init (msg_youconnect_addrParser *o, uint8_t *buf, int buf_len); +static int msg_youconnect_addrParser_GotEverything (msg_youconnect_addrParser *o); +static int msg_youconnect_addrParser_Getname (msg_youconnect_addrParser *o, uint8_t **data, int *data_len); +static void msg_youconnect_addrParser_Resetname (msg_youconnect_addrParser *o); +static void msg_youconnect_addrParser_Forwardname (msg_youconnect_addrParser *o); +static int msg_youconnect_addrParser_Getaddr (msg_youconnect_addrParser *o, uint8_t **data, int *data_len); +static void msg_youconnect_addrParser_Resetaddr (msg_youconnect_addrParser *o); +static void msg_youconnect_addrParser_Forwardaddr (msg_youconnect_addrParser *o); + +void msg_youconnect_addrWriter_Init (msg_youconnect_addrWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->name_count = 0; + o->addr_count = 0; +} + +int msg_youconnect_addrWriter_Finish (msg_youconnect_addrWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->name_count == 1) + ASSERT(o->addr_count == 1) + + return o->used; +} + +uint8_t * msg_youconnect_addrWriter_Addname (msg_youconnect_addrWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->name_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->name_count++; + + return dest; +} + +uint8_t * msg_youconnect_addrWriter_Addaddr (msg_youconnect_addrWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->addr_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->addr_count++; + + return dest; +} + +int msg_youconnect_addrParser_Init (msg_youconnect_addrParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->name_start = o->buf_len; + o->name_span = 0; + o->name_pos = 0; + o->addr_start = o->buf_len; + o->addr_span = 0; + o->addr_pos = 0; + + int name_count = 0; + int addr_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 1: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->name_start == o->buf_len) { + o->name_start = entry_pos; + } + o->name_span = pos - o->name_start; + name_count++; + break; + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->addr_start == o->buf_len) { + o->addr_start = entry_pos; + } + o->addr_span = pos - o->addr_start; + addr_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(name_count == 1)) { + return 0; + } + if (!(addr_count == 1)) { + return 0; + } + + return 1; +} + +int msg_youconnect_addrParser_GotEverything (msg_youconnect_addrParser *o) +{ + return ( + o->name_pos == o->name_span + && + o->addr_pos == o->addr_span + ); +} + +int msg_youconnect_addrParser_Getname (msg_youconnect_addrParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->name_pos >= 0) + ASSERT(o->name_pos <= o->name_span) + + int left = o->name_span - o->name_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->name_start + o->name_pos, sizeof(header)); + o->name_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->name_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->name_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->name_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->name_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->name_start + o->name_pos, sizeof(val)); + o->name_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->name_start + o->name_pos; + o->name_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 1) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnect_addrParser_Resetname (msg_youconnect_addrParser *o) +{ + o->name_pos = 0; +} + +void msg_youconnect_addrParser_Forwardname (msg_youconnect_addrParser *o) +{ + o->name_pos = o->name_span; +} + +int msg_youconnect_addrParser_Getaddr (msg_youconnect_addrParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->addr_pos >= 0) + ASSERT(o->addr_pos <= o->addr_span) + + int left = o->addr_span - o->addr_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->addr_start + o->addr_pos, sizeof(header)); + o->addr_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->addr_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->addr_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->addr_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->addr_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->addr_start + o->addr_pos, sizeof(val)); + o->addr_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->addr_start + o->addr_pos; + o->addr_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_youconnect_addrParser_Resetaddr (msg_youconnect_addrParser *o) +{ + o->addr_pos = 0; +} + +void msg_youconnect_addrParser_Forwardaddr (msg_youconnect_addrParser *o) +{ + o->addr_pos = o->addr_span; +} + +#define msg_seed_SIZEseed_id (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) +#define msg_seed_SIZEkey(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) +#define msg_seed_SIZEiv(_len) (sizeof(struct BProto_header_s) + sizeof(struct BProto_data_header_s) + (_len)) + +typedef struct { + uint8_t *out; + int used; + int seed_id_count; + int key_count; + int iv_count; +} msg_seedWriter; + +static void msg_seedWriter_Init (msg_seedWriter *o, uint8_t *out); +static int msg_seedWriter_Finish (msg_seedWriter *o); +static void msg_seedWriter_Addseed_id (msg_seedWriter *o, uint16_t v); +static uint8_t * msg_seedWriter_Addkey (msg_seedWriter *o, int len); +static uint8_t * msg_seedWriter_Addiv (msg_seedWriter *o, int len); + +typedef struct { + uint8_t *buf; + int buf_len; + int seed_id_start; + int seed_id_span; + int seed_id_pos; + int key_start; + int key_span; + int key_pos; + int iv_start; + int iv_span; + int iv_pos; +} msg_seedParser; + +static int msg_seedParser_Init (msg_seedParser *o, uint8_t *buf, int buf_len); +static int msg_seedParser_GotEverything (msg_seedParser *o); +static int msg_seedParser_Getseed_id (msg_seedParser *o, uint16_t *v); +static void msg_seedParser_Resetseed_id (msg_seedParser *o); +static void msg_seedParser_Forwardseed_id (msg_seedParser *o); +static int msg_seedParser_Getkey (msg_seedParser *o, uint8_t **data, int *data_len); +static void msg_seedParser_Resetkey (msg_seedParser *o); +static void msg_seedParser_Forwardkey (msg_seedParser *o); +static int msg_seedParser_Getiv (msg_seedParser *o, uint8_t **data, int *data_len); +static void msg_seedParser_Resetiv (msg_seedParser *o); +static void msg_seedParser_Forwardiv (msg_seedParser *o); + +void msg_seedWriter_Init (msg_seedWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->seed_id_count = 0; + o->key_count = 0; + o->iv_count = 0; +} + +int msg_seedWriter_Finish (msg_seedWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 1) + ASSERT(o->key_count == 1) + ASSERT(o->iv_count == 1) + + return o->used; +} + +void msg_seedWriter_Addseed_id (msg_seedWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->seed_id_count++; +} + +uint8_t * msg_seedWriter_Addkey (msg_seedWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->key_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(2); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->key_count++; + + return dest; +} + +uint8_t * msg_seedWriter_Addiv (msg_seedWriter *o, int len) +{ + ASSERT(o->used >= 0) + ASSERT(o->iv_count == 0) + ASSERT(len >= 0 && len <= UINT32_MAX) + + struct BProto_header_s header; + header.id = htol16(3); + header.type = htol16(BPROTO_TYPE_DATA); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_data_header_s data; + data.len = htol32(len); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_data_header_s); + + uint8_t *dest = (o->out + o->used); + o->used += len; + + o->iv_count++; + + return dest; +} + +int msg_seedParser_Init (msg_seedParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->seed_id_start = o->buf_len; + o->seed_id_span = 0; + o->seed_id_pos = 0; + o->key_start = o->buf_len; + o->key_span = 0; + o->key_pos = 0; + o->iv_start = o->buf_len; + o->iv_span = 0; + o->iv_pos = 0; + + int seed_id_count = 0; + int key_count = 0; + int iv_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->seed_id_start == o->buf_len) { + o->seed_id_start = entry_pos; + } + o->seed_id_span = pos - o->seed_id_start; + seed_id_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + case 2: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->key_start == o->buf_len) { + o->key_start = entry_pos; + } + o->key_span = pos - o->key_start; + key_count++; + break; + case 3: + if (!(type == BPROTO_TYPE_DATA)) { + return 0; + } + if (o->iv_start == o->buf_len) { + o->iv_start = entry_pos; + } + o->iv_span = pos - o->iv_start; + iv_count++; + break; + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(seed_id_count == 1)) { + return 0; + } + if (!(key_count == 1)) { + return 0; + } + if (!(iv_count == 1)) { + return 0; + } + + return 1; +} + +int msg_seedParser_GotEverything (msg_seedParser *o) +{ + return ( + o->seed_id_pos == o->seed_id_span + && + o->key_pos == o->key_span + && + o->iv_pos == o->iv_span + ); +} + +int msg_seedParser_Getseed_id (msg_seedParser *o, uint16_t *v) +{ + ASSERT(o->seed_id_pos >= 0) + ASSERT(o->seed_id_pos <= o->seed_id_span) + + int left = o->seed_id_span - o->seed_id_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(header)); + o->seed_id_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->seed_id_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->seed_id_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->seed_id_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->seed_id_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetseed_id (msg_seedParser *o) +{ + o->seed_id_pos = 0; +} + +void msg_seedParser_Forwardseed_id (msg_seedParser *o) +{ + o->seed_id_pos = o->seed_id_span; +} + +int msg_seedParser_Getkey (msg_seedParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->key_pos >= 0) + ASSERT(o->key_pos <= o->key_span) + + int left = o->key_span - o->key_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->key_start + o->key_pos, sizeof(header)); + o->key_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->key_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->key_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->key_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->key_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->key_start + o->key_pos, sizeof(val)); + o->key_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->key_start + o->key_pos; + o->key_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 2) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetkey (msg_seedParser *o) +{ + o->key_pos = 0; +} + +void msg_seedParser_Forwardkey (msg_seedParser *o) +{ + o->key_pos = o->key_span; +} + +int msg_seedParser_Getiv (msg_seedParser *o, uint8_t **data, int *data_len) +{ + ASSERT(o->iv_pos >= 0) + ASSERT(o->iv_pos <= o->iv_span) + + int left = o->iv_span - o->iv_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->iv_start + o->iv_pos, sizeof(header)); + o->iv_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->iv_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + o->iv_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->iv_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->iv_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->iv_start + o->iv_pos, sizeof(val)); + o->iv_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + uint8_t *payload = o->buf + o->iv_start + o->iv_pos; + o->iv_pos += payload_len; + left -= payload_len; + + if (type == BPROTO_TYPE_DATA && id == 3) { + *data = payload; + *data_len = payload_len; + return 1; + } + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_seedParser_Resetiv (msg_seedParser *o) +{ + o->iv_pos = 0; +} + +void msg_seedParser_Forwardiv (msg_seedParser *o) +{ + o->iv_pos = o->iv_span; +} + +#define msg_confirmseed_SIZEseed_id (sizeof(struct BProto_header_s) + sizeof(struct BProto_uint16_s)) + +typedef struct { + uint8_t *out; + int used; + int seed_id_count; +} msg_confirmseedWriter; + +static void msg_confirmseedWriter_Init (msg_confirmseedWriter *o, uint8_t *out); +static int msg_confirmseedWriter_Finish (msg_confirmseedWriter *o); +static void msg_confirmseedWriter_Addseed_id (msg_confirmseedWriter *o, uint16_t v); + +typedef struct { + uint8_t *buf; + int buf_len; + int seed_id_start; + int seed_id_span; + int seed_id_pos; +} msg_confirmseedParser; + +static int msg_confirmseedParser_Init (msg_confirmseedParser *o, uint8_t *buf, int buf_len); +static int msg_confirmseedParser_GotEverything (msg_confirmseedParser *o); +static int msg_confirmseedParser_Getseed_id (msg_confirmseedParser *o, uint16_t *v); +static void msg_confirmseedParser_Resetseed_id (msg_confirmseedParser *o); +static void msg_confirmseedParser_Forwardseed_id (msg_confirmseedParser *o); + +void msg_confirmseedWriter_Init (msg_confirmseedWriter *o, uint8_t *out) +{ + o->out = out; + o->used = 0; + o->seed_id_count = 0; +} + +int msg_confirmseedWriter_Finish (msg_confirmseedWriter *o) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 1) + + return o->used; +} + +void msg_confirmseedWriter_Addseed_id (msg_confirmseedWriter *o, uint16_t v) +{ + ASSERT(o->used >= 0) + ASSERT(o->seed_id_count == 0) + + + struct BProto_header_s header; + header.id = htol16(1); + header.type = htol16(BPROTO_TYPE_UINT16); + memcpy(o->out + o->used, &header, sizeof(header)); + o->used += sizeof(struct BProto_header_s); + + struct BProto_uint16_s data; + data.v = htol16(v); + memcpy(o->out + o->used, &data, sizeof(data)); + o->used += sizeof(struct BProto_uint16_s); + + o->seed_id_count++; +} + +int msg_confirmseedParser_Init (msg_confirmseedParser *o, uint8_t *buf, int buf_len) +{ + ASSERT(buf_len >= 0) + + o->buf = buf; + o->buf_len = buf_len; + o->seed_id_start = o->buf_len; + o->seed_id_span = 0; + o->seed_id_pos = 0; + + int seed_id_count = 0; + + int pos = 0; + int left = o->buf_len; + + while (left > 0) { + int entry_pos = pos; + + if (!(left >= sizeof(struct BProto_header_s))) { + return 0; + } + struct BProto_header_s header; + memcpy(&header, o->buf + pos, sizeof(header)); + pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + if (!(left >= sizeof(struct BProto_uint8_s))) { + return 0; + } + pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT16: { + if (!(left >= sizeof(struct BProto_uint16_s))) { + return 0; + } + pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + switch (id) { + case 1: + if (o->seed_id_start == o->buf_len) { + o->seed_id_start = entry_pos; + } + o->seed_id_span = pos - o->seed_id_start; + seed_id_count++; + break; + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT32: { + if (!(left >= sizeof(struct BProto_uint32_s))) { + return 0; + } + pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_UINT64: { + if (!(left >= sizeof(struct BProto_uint64_s))) { + return 0; + } + pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + + switch (id) { + default: + return 0; + } + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + if (!(left >= sizeof(struct BProto_data_header_s))) { + return 0; + } + struct BProto_data_header_s val; + memcpy(&val, o->buf + pos, sizeof(val)); + pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + if (!(left >= payload_len)) { + return 0; + } + pos += payload_len; + left -= payload_len; + + switch (id) { + default: + return 0; + } + } break; + default: + return 0; + } + } + + if (!(seed_id_count == 1)) { + return 0; + } + + return 1; +} + +int msg_confirmseedParser_GotEverything (msg_confirmseedParser *o) +{ + return ( + o->seed_id_pos == o->seed_id_span + ); +} + +int msg_confirmseedParser_Getseed_id (msg_confirmseedParser *o, uint16_t *v) +{ + ASSERT(o->seed_id_pos >= 0) + ASSERT(o->seed_id_pos <= o->seed_id_span) + + int left = o->seed_id_span - o->seed_id_pos; + + while (left > 0) { + ASSERT(left >= sizeof(struct BProto_header_s)) + struct BProto_header_s header; + memcpy(&header, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(header)); + o->seed_id_pos += sizeof(struct BProto_header_s); + left -= sizeof(struct BProto_header_s); + uint16_t type = ltoh16(header.type); + uint16_t id = ltoh16(header.id); + + switch (type) { + case BPROTO_TYPE_UINT8: { + ASSERT(left >= sizeof(struct BProto_uint8_s)) + o->seed_id_pos += sizeof(struct BProto_uint8_s); + left -= sizeof(struct BProto_uint8_s); + } break; + case BPROTO_TYPE_UINT16: { + ASSERT(left >= sizeof(struct BProto_uint16_s)) + struct BProto_uint16_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_uint16_s); + left -= sizeof(struct BProto_uint16_s); + + if (id == 1) { + *v = ltoh16(val.v); + return 1; + } + } break; + case BPROTO_TYPE_UINT32: { + ASSERT(left >= sizeof(struct BProto_uint32_s)) + o->seed_id_pos += sizeof(struct BProto_uint32_s); + left -= sizeof(struct BProto_uint32_s); + } break; + case BPROTO_TYPE_UINT64: { + ASSERT(left >= sizeof(struct BProto_uint64_s)) + o->seed_id_pos += sizeof(struct BProto_uint64_s); + left -= sizeof(struct BProto_uint64_s); + } break; + case BPROTO_TYPE_DATA: + case BPROTO_TYPE_CONSTDATA: + { + ASSERT(left >= sizeof(struct BProto_data_header_s)) + struct BProto_data_header_s val; + memcpy(&val, o->buf + o->seed_id_start + o->seed_id_pos, sizeof(val)); + o->seed_id_pos += sizeof(struct BProto_data_header_s); + left -= sizeof(struct BProto_data_header_s); + + uint32_t payload_len = ltoh32(val.len); + ASSERT(left >= payload_len) + o->seed_id_pos += payload_len; + left -= payload_len; + } break; + default: + ASSERT(0); + } + } + + return 0; +} + +void msg_confirmseedParser_Resetseed_id (msg_confirmseedParser *o) +{ + o->seed_id_pos = 0; +} + +void msg_confirmseedParser_Forwardseed_id (msg_confirmseedParser *o) +{ + o->seed_id_pos = o->seed_id_span; +} + diff --git a/external/badvpn_dns/generated/flex_BPredicate.c b/external/badvpn_dns/generated/flex_BPredicate.c new file mode 100644 index 00000000..95f4ff68 --- /dev/null +++ b/external/badvpn_dns/generated/flex_BPredicate.c @@ -0,0 +1,2143 @@ +#line 2 "generated//flex_BPredicate.c" + +#line 4 "generated//flex_BPredicate.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if 1 + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +static void yyensure_buffer_stack (yyscan_t yyscanner ); +static void yy_load_buffer_state (yyscan_t yyscanner ); +static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 13 +#define YY_END_OF_BUFFER 14 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[34] = + { 0, + 0, 0, 14, 12, 11, 11, 12, 1, 2, 3, + 9, 9, 9, 9, 9, 9, 11, 0, 10, 9, + 9, 9, 5, 9, 9, 4, 6, 9, 9, 9, + 7, 8, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 1, 1, 1, 1, 1, 5, + 6, 1, 1, 7, 1, 1, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 9, 8, 8, 10, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 11, 12, 8, + 8, 13, 8, 14, 8, 8, 8, 8, 8, 8, + 1, 1, 1, 1, 8, 1, 15, 8, 8, 8, + + 16, 17, 8, 8, 8, 8, 8, 18, 8, 8, + 8, 8, 8, 19, 20, 21, 22, 8, 8, 8, + 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[23] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2 + } ; + +static yyconst flex_int16_t yy_base[36] = + { 0, + 0, 0, 46, 47, 21, 23, 41, 47, 47, 47, + 0, 33, 31, 29, 26, 21, 25, 35, 47, 0, + 28, 23, 0, 18, 13, 0, 0, 14, 17, 16, + 0, 0, 47, 28, 29 + } ; + +static yyconst flex_int16_t yy_def[36] = + { 0, + 33, 1, 33, 33, 33, 33, 34, 33, 33, 33, + 35, 35, 35, 35, 35, 35, 33, 34, 33, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 0, 33, 33 + } ; + +static yyconst flex_int16_t yy_nxt[70] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, + 13, 14, 11, 11, 11, 11, 15, 11, 11, 11, + 16, 11, 17, 17, 17, 17, 17, 17, 18, 18, + 20, 32, 31, 30, 29, 28, 27, 26, 19, 25, + 24, 23, 22, 21, 19, 33, 3, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33 + } ; + +static yyconst flex_int16_t yy_chk[70] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 5, 5, 6, 6, 17, 17, 34, 34, + 35, 30, 29, 28, 25, 24, 22, 21, 18, 16, + 15, 14, 13, 12, 7, 3, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "predicate/BPredicate.l" +/** + * @file BPredicate.l + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} lexer file. + */ +#line 35 "predicate/BPredicate.l" + +#include +#include + +#include +#include + +#include + +#define YY_INPUT(buffer, res, max_size) \ + int bytes_read = LexMemoryBufferInput_Read((LexMemoryBufferInput *)yyget_extra(yyscanner), buffer, max_size); \ + res = (bytes_read == 0 ? YY_NULL : bytes_read); + +#define YY_NO_UNISTD_H 1 +#line 503 "generated//flex_BPredicate.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + yy_size_t yy_n_chars; + yy_size_t yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + + static void yy_push_state (int new_state ,yyscan_t yyscanner); + + static void yy_pop_state (yyscan_t yyscanner ); + + static int yy_top_state (yyscan_t yyscanner ); + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 52 "predicate/BPredicate.l" + +#line 756 "generated//flex_BPredicate.c" + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 33 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 53 "predicate/BPredicate.l" +return SPAR; + YY_BREAK +case 2: +YY_RULE_SETUP +#line 54 "predicate/BPredicate.l" +return EPAR; + YY_BREAK +case 3: +YY_RULE_SETUP +#line 55 "predicate/BPredicate.l" +return COMMA; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 56 "predicate/BPredicate.l" +return AND; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 57 "predicate/BPredicate.l" +return OR; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 58 "predicate/BPredicate.l" +return NOT; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 59 "predicate/BPredicate.l" +return CONSTANT_TRUE; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 60 "predicate/BPredicate.l" +return CONSTANT_FALSE; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 61 "predicate/BPredicate.l" +{ + int l = strlen(yytext); + char *p = (char *)malloc(l + 1); + if (p) { + memcpy(p, yytext, l); + p[l] = '\0'; + } + yylval->text = p; + return NAME; + } + YY_BREAK +case 10: +/* rule 10 can match eol */ +YY_RULE_SETUP +#line 71 "predicate/BPredicate.l" +{ + int l = strlen(yytext); + char *p = (char *)malloc(l - 1); + if (p) { + memcpy(p, yytext + 1, l - 2); + p[l - 2] = '\0'; + } + yylval->text = p; + return STRING; + } + YY_BREAK +case 11: +/* rule 11 can match eol */ +YY_RULE_SETUP +#line 81 "predicate/BPredicate.l" +; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 82 "predicate/BPredicate.l" +LexMemoryBufferInput_SetError((LexMemoryBufferInput *)yyget_extra(yyscanner)); return 0; // remember failure and report EOF + YY_BREAK +case 13: +YY_RULE_SETUP +#line 83 "predicate/BPredicate.l" +ECHO; + YY_BREAK +#line 924 "generated//flex_BPredicate.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + yy_size_t num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + yy_size_t new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 34 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 33); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register yy_size_t number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + yy_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree((void *) b->yy_ch_buf ,yyscanner ); + + yyfree((void *) b ,yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) yyalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + + static void yy_push_state (int new_state , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth ) + { + yy_size_t new_size; + + yyg->yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yyg->yy_start_stack_depth * sizeof( int ); + + if ( ! yyg->yy_start_stack ) + yyg->yy_start_stack = (int *) yyalloc(new_size ,yyscanner ); + + else + yyg->yy_start_stack = (int *) yyrealloc((void *) yyg->yy_start_stack,new_size ,yyscanner ); + + if ( ! yyg->yy_start_stack ) + YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); + } + + yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); +} + + static void yy_pop_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( --yyg->yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]); +} + + static int yy_top_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyg->yy_start_stack[yyg->yy_start_stack_ptr - 1]; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +yy_size_t yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void yyset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void yyset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int yylex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ + +int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 83 "predicate/BPredicate.l" + + + diff --git a/external/badvpn_dns/generated/flex_BPredicate.h b/external/badvpn_dns/generated/flex_BPredicate.h new file mode 100644 index 00000000..f3d24285 --- /dev/null +++ b/external/badvpn_dns/generated/flex_BPredicate.h @@ -0,0 +1,350 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 6 "generated//flex_BPredicate.h" + +#line 8 "generated//flex_BPredicate.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 37 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if 1 + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + yy_size_t yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart (FILE *input_file ,yyscan_t yyscanner ); +void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void yypop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); + +void *yyalloc (yy_size_t ,yyscan_t yyscanner ); +void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void yyfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define yywrap(yyscanner) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy (yyscan_t yyscanner ); + +int yyget_debug (yyscan_t yyscanner ); + +void yyset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); + +void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *yyget_in (yyscan_t yyscanner ); + +void yyset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *yyget_out (yyscan_t yyscanner ); + +void yyset_out (FILE * out_str ,yyscan_t yyscanner ); + +yy_size_t yyget_leng (yyscan_t yyscanner ); + +char *yyget_text (yyscan_t yyscanner ); + +int yyget_lineno (yyscan_t yyscanner ); + +void yyset_lineno (int line_number ,yyscan_t yyscanner ); + +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + +YYSTYPE * yyget_lval (yyscan_t yyscanner ); + +void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc (yyscan_t yyscanner ); + + void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap (yyscan_t yyscanner ); +#else +extern int yywrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 83 "predicate/BPredicate.l" + + +#line 349 "generated//flex_BPredicate.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/external/badvpn_dns/lemon/lemon.c b/external/badvpn_dns/lemon/lemon.c new file mode 100644 index 00000000..8336e9a1 --- /dev/null +++ b/external/badvpn_dns/lemon/lemon.c @@ -0,0 +1,4889 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +#ifdef __WIN32__ +extern int access(); +#else +#include +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +static char *msort(char*,char**,int(*)(const char*,const char*)); + +/* +** Compilers are getting increasingly pedantic about type conversions +** as C evolves ever closer to Ada.... To work around the latest problems +** we have to define the following variant of strlen(). +*/ +#define lemonStrlen(X) ((int)strlen(X)) + +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + char *arg; + char *message; +}; +int OptInit(/* char**,struct s_options*,FILE* */); +int OptNArgs(/* void */); +char *OptArg(/* int */); +void OptErr(/* int */); +void OptPrint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); +void ResortStates(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL, + MULTITERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if precedence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destLineno; /* Line number for start of destructor */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + RRCONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int statenum; /* Sequential number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + struct symbol *wildcard; /* Token that matches anything */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + char *error; /* Code to execute when an error is seen */ + char *overflow; /* Code to execute on a stack overflow */ + char *failure; /* Code to execute on parser failure */ + char *accept; /* Code to execute when the parser excepts */ + char *extracode; /* Code appended to the generated file */ + char *tokendest; /* Code to execute to destroy token data */ + char *vardest; /* Code for the default non-terminal destructor */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammar */ + int nolinenosflag; /* True if #line statements should not be printed */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +static struct action *Action_new(void){ + static struct action *freelist = 0; + struct action *new; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)calloc(amt, sizeof(struct action)); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp( + struct action *ap1, + struct action *ap2 +){ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ){ + rc = (int)ap1->type - (int)ap2->type; + } + if( rc==0 && ap1->type==REDUCE ){ + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +static struct action *Action_sort( + struct action *ap +){ + ap = (struct action *)msort((char *)ap,(char **)&ap->next, + (int(*)(const char*,const char*))actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +char *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure +*/ +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ + } *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = calloc( 1, sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset where we can + ** insert the current transaction set. Fall out of the loop when that + ** offset is found. In the worst case, we fall out of the loop when + ** i reaches p->nAction, which means we append the new transaction set. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=0; inAction+p->mnLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + }else if( p->aAction[i].lookahead==p->mnLookahead ){ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* Same as a prior transaction set */ + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i, j; + for(i=0; inrhs && rp->precsym==0; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + if( sp->subsym[j]->prec>=0 ){ + rp->precsym = sp->subsym[j]; + break; + } + } + }else if( sp->prec>=0 ){ + rp->precsym = rp->rhs[i]; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i, j; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = LEMON_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type!=TERMINAL || sp->lambda==LEMON_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = LEMON_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s2->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + progress += SetAdd(s1->firstset,s2->subsym[j]->index); + } + break; + }else if( s1==s2 ){ + if( s1->lambda==LEMON_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==LEMON_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + rp->lhsStart = 1; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* +** Return true if two symbols are the same. +*/ +int same_symbol(a,b) +struct symbol *a; +struct symbol *b; +{ + int i; + if( a==b ) return 1; + if( a->type!=MULTITERMINAL ) return 0; + if( b->type!=MULTITERMINAL ) return 0; + if( a->nsubsym!=b->nsubsym ) return 0; + for(i=0; insubsym; i++){ + if( a->subsym[i]!=b->subsym[i] ) return 0; + } + return 1; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + if( sp->type==MULTITERMINAL ){ + int i; + for(i=0; insubsym; i++){ + Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); + } + }else{ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + /* assert( stp->ap ); */ + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolved, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==SHIFT ){ + apy->type = SSCONFLICT; + errcnt++; + } + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = SRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = SRCONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = RRCONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==SSCONFLICT || + apx->type==SRCONFLICT || + apx->type==RRCONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==SSCONFLICT || + apy->type==SRCONFLICT || + apy->type==RRCONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)calloc( amt, sizeof(struct config) ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else if( xsp->type==MULTITERMINAL ){ + int k; + for(k=0; knsubsym; k++){ + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==LEMON_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = lemonStrlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = lemonStrlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = malloc( lemonStrlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + + +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static int nolinenosflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, + {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + memset(&lem, 0, sizeof(lem)); + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.nolinenosflag = nolinenosflag; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + lem.errsym->useCnt = 0; + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.nrule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal+1); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Reorder and renumber the states so that states with fewer choices + ** occur at the end. */ + ResortStates(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); + return (lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((unsigned long)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge( + char *a, + char *b, + int (*cmp)(const char*,const char*), + int offset +){ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +static char *msort( + char *list, + char **next, + int (*cmp)(const char*,const char*) +){ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is no prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)calloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbols on RHS of rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){ + struct symbol *msp = psp->rhs[psp->nrhs-1]; + if( msp->type!=MULTITERMINAL ){ + struct symbol *origsp = msp; + msp = calloc(1,sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = calloc(1,sizeof(struct symbol*)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs-1] = msp; + } + msp->nsubsym++; + msp->subsym = realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym); + msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); + if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllinenoslot = 0; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + psp->insertLineMacro = 0; + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + psp->insertLineMacro = 0; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + psp->insertLineMacro = 0; + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + psp->insertLineMacro = 0; + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + psp->insertLineMacro = 0; + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + psp->insertLineMacro = 0; + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else if( strcmp(x,"wildcard")==0 ){ + psp->state = WAITING_FOR_WILDCARD_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->insertLineMacro = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ + char *zOld, *zNew, *zBuf, *z; + int nOld, n, nLine, nNew, nBack; + int addLineMacro; + char zLine[50]; + zNew = x; + if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; + nNew = lemonStrlen(zNew); + if( *psp->declargslot ){ + zOld = *psp->declargslot; + }else{ + zOld = ""; + } + nOld = lemonStrlen(zOld); + n = nOld + nNew + 20; + addLineMacro = !psp->gp->nolinenosflag && psp->insertLineMacro && + (psp->decllinenoslot==0 || psp->decllinenoslot[0]!=0); + if( addLineMacro ){ + for(z=psp->filename, nBack=0; *z; z++){ + if( *z=='\\' ) nBack++; + } + sprintf(zLine, "#line %d ", psp->tokenlineno); + nLine = lemonStrlen(zLine); + n += nLine + lemonStrlen(psp->filename) + nBack; + } + *psp->declargslot = zBuf = realloc(*psp->declargslot, n); + zBuf += nOld; + if( addLineMacro ){ + if( nOld && zBuf[-1]!='\n' ){ + *(zBuf++) = '\n'; + } + memcpy(zBuf, zLine, nLine); + zBuf += nLine; + *(zBuf++) = '"'; + for(z=psp->filename; *z; z++){ + if( *z=='\\' ){ + *(zBuf++) = '\\'; + } + *(zBuf++) = *z; + } + *(zBuf++) = '"'; + *(zBuf++) = '\n'; + } + if( psp->decllinenoslot && psp->decllinenoslot[0]==0 ){ + psp->decllinenoslot[0] = psp->tokenlineno; + } + memcpy(zBuf, zNew, nNew); + zBuf += nNew; + *zBuf = 0; + psp->state = WAITING_FOR_DECL_OR_RULE; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case WAITING_FOR_WILDCARD_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%wildcard argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->gp->wildcard==0 ){ + psp->gp->wildcard = sp; + }else{ + ErrorMsg(psp->filename, psp->tokenlineno, + "Extra wildcard to token: %s", x); + psp->errorcnt++; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the preprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static void preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start = 0; + int lineno = 1; + int start_lineno = 1; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){ + cp += 2; + while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( lemonStrlen(lemp->filename) + lemonStrlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = lemonStrlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); + /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + sp = rp->rhs[i]; + printf(" %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + printf("|%s", sp->subsym[j]->name); + } + } + /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); + /* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + struct symbol *sp; + int i, j; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + sp = rp->rhs[i]; + fprintf(fp," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + fprintf(fp,"|%s",sp->subsym[j]->name); + } + } + } +} + +/* #define TEST */ +#if 0 +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case SRCONFLICT: + case RRCONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SSCONFLICT: + fprintf(fp,"%*s shift %d ** Parsing conflict **", + indent,ap->sp->name,ap->x.stp->statenum); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->statenum); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#if 0 + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + for(i=0; insymbol; i++){ + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if( sp->type==NONTERMINAL ){ + fprintf(fp, ":"); + if( sp->lambda ){ + fprintf(fp, " "); + } + for(j=0; jnterminal; j++){ + if( sp->firstset && SetFind(sp->firstset, j) ){ + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + fprintf(fp, "\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + extern char *getenv(); + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[lemonStrlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->statenum; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(out,lineno,filename) +FILE *out; +int lineno; +char *filename; +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int *lineno; +{ + if( str==0 ) return; + while( *str ){ + putc(*str,out); + if( *str=='\n' ) (*lineno)++; + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp = 0; + + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else if( sp->destructor ){ + cp = sp->destructor; + fprintf(out,"{\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,sp->destLineno,lemp->filename); } + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + fprintf(out,"{\n"); (*lineno)++; + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) (*lineno)++; + fputc(*cp,out); + } + fprintf(out,"\n"); (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); + } + fprintf(out,"}\n"); (*lineno)++; + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(char *zText, int n, int p1, int p2){ + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = lemonStrlen(zText); + } + if( n+sizeof(zInt)*2+used >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = realloc(z, alloced); + } + if( z==0 ) return ""; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && n>0 && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += lemonStrlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + if( rp->code==0 ){ + rp->code = "\n"; + rp->line = rp->ruleline; + } + + append_str(0,0,0,0); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + struct symbol *sp = rp->rhs[i]; + int dtnum; + if( sp->type==MULTITERMINAL ){ + dtnum = sp->subsym[0]->dtnum; + }else{ + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + if( rp->code ){ + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp?cp:""); + } +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp; + + /* Generate code to do the reduce action */ + if( rp->code ){ + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,rp->line,lemp->filename); } + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) (*lineno)++; + } /* End loop */ + fprintf(out,"}\n"); (*lineno)++; + if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); } + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)calloc( arraysize, sizeof(char*) ); + for(i=0; ivartype ){ + maxdtlength = lemonStrlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = lemonStrlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + if( lemp->tokentype && strcmp(stddt, lemp->tokentype)==0 ){ + sp->dtnum = 0; + continue; + } + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( lemonStrlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," int yyinit;\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->useCnt ){ + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + } + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + return p2->nAction - p1->nAction; +} + +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp){ + int j; + fprintf(out,"%s ::=", rp->lhs->name); + for(j=0; jnrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; knsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } +} + + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+1)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + if( lemp->wildcard ){ + fprintf(out,"#define YYWILDCARD %d\n", + lemp->wildcard->index); lineno++; + } + print_stack_union(out,lemp,&lineno,mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++; + if( lemp->stacksize ){ + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + fprintf(out, "#endif\n"); lineno++; + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = lemonStrlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + if( lemp->errsym->useCnt ){ + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + } + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = calloc(lemp->nstate*2, sizeof(ax[0])); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + n = acttab_size(pActtab); + for(i=j=0; instate + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_SHIFT_MAX %d\n", n-1); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + n = lemp->nstate; + while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--; + fprintf(out, "#define YY_REDUCE_MAX %d\n", n-1); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + int mx = lemp->nterminal - 1; + while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } + for(i=0; i<=mx; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammar. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"", i); + writeRuleText(out, rp); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + if( once ){ + fprintf(out, " /* TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + int once = 1; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + if( once ){ + fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++; + once = 0; + } + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + } + fprintf(out," break;\n"); lineno++; + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d: /* %s */\n", + sp2->index, sp2->name); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + translate_code(lemp, rp); + } + /* First output rules other than the default: rule */ + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; /* Other rules with the same action */ + if( rp->code==0 ) continue; + if( rp->code[0]=='\n' && rp->code[1]==0 ) continue; /* Will be default: */ + fprintf(out," case %d: /* ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */\n"); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d: /* ", rp2->index); + writeRuleText(out, rp2); + fprintf(out," */ yytestcase(yyruleno==%d);\n", rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + rp->code = 0; + } + /* Finally, output the default: rule. We choose as the default: all + ** empty actions. */ + fprintf(out," default:\n"); lineno++; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code==0 ) continue; + assert( rp->code[0]=='\n' && rp->code[1]==0 ); + fprintf(out," /* (%d) ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->index); lineno++; + } + fprintf(out," break;\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Except, there is no default if the wildcard token +** is a possible look-ahead. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + int usesWildcard; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + usesWildcard = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==SHIFT && ap->sp==lemp->wildcard ){ + usesWildcard = 1; + } + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp->lhsStart ) continue; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 1 or if the wildcard token is a possible + ** lookahead. + */ + if( nbest<1 || usesWildcard ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + + +/* +** Compare two states for sorting purposes. The smaller state is the +** one with the most non-terminal actions. If they have the same number +** of non-terminal actions, then the smaller is the one with the most +** token actions. +*/ +static int stateResortCompare(const void *a, const void *b){ + const struct state *pA = *(const struct state**)a; + const struct state *pB = *(const struct state**)b; + int n; + + n = pB->nNtAct - pA->nNtAct; + if( n==0 ){ + n = pB->nTknAct - pA->nTknAct; + } + return n; +} + + +/* +** Renumber and resort states so that states with fewer choices +** occur at the end. Except, keep state 0 as the first state. +*/ +void ResortStates(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct action *ap; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + } + qsort(&lemp->sorted[1], lemp->nstate-1, sizeof(lemp->sorted[0]), + stateResortCompare); + for(i=0; instate; i++){ + lemp->sorted[i]->statenum = i; + } +} + + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + s = (char*)calloc( size, 1); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + return s; +} + +/* Deallocate a set */ +void SetFree(s) +char *s; +{ + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(s,e) +char *s; +int e; +{ + int rv; + assert( e>=0 && esize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)calloc(1, sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = LEMON_FALSE; + sp->destructor = 0; + sp->destLineno = 0; + sp->datatype = 0; + sp->useCnt = 0; + Symbol_insert(sp,sp->name); + } + sp->useCnt++; + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(struct symbol **a, struct symbol **b){ + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)calloc(size, sizeof(struct symbol *)); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)calloc(1, sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/external/badvpn_dns/lemon/lempar.c b/external/badvpn_dns/lemon/lempar.c new file mode 100644 index 00000000..774b875e --- /dev/null +++ b/external/badvpn_dns/lemon/lempar.c @@ -0,0 +1,842 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is included that follows the "include" declaration +** in the input grammar file. */ +#include +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* The yyzerominor constant is used to initialize instances of +** YYMINORTYPE objects to zero. */ +static const YYMINORTYPE yyzerominor = { 0 }; + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% +#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyidxMax; /* Maximum value of yyidx */ +#endif + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. +*/ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } +#endif + } +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; +#ifdef YYTRACKMAXSTACKDEPTH + pParser->yyidxMax = 0; +#endif +#if YYSTACKDEPTH<=0 + pParser->yystack = NULL; + pParser->yystksz = 0; + yyGrowStack(pParser); +#endif + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH; + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor(pParser, yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
    +**
  • A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +**
  • A pointer to a function used to reclaim memory obtained +** from malloc. +**
+*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif + (*freeProc)((void*)pParser); +} + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyidxMax; +} +#endif + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( iLookAhead>0 ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + if( j>=0 && j %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + } + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + int stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_MAX ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_MAX ); +#endif + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; +#ifdef YYTRACKMAXSTACKDEPTH + if( yypParser->yyidx>yypParser->yyidxMax ){ + yypParser->yyidxMax = yypParser->yyidx; + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yyidx>=YYSTACKDEPTH ){ + yyStackOverflow(yypParser, yypMinor); + return; + } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = (YYACTIONTYPE)yyNewState; + yytos->major = (YYCODETYPE)yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static const struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } +#endif /* NDEBUG */ + + /* Silence complaints from purify about yygotominor being uninitialized + ** in some cases when it is copied into the stack after the following + ** switch. yygotominor is uninitialized when a rule reduces that does + ** not set the value of its left-hand side nonterminal. Leaving the + ** value of the nonterminal uninitialized is utterly harmless as long + ** as the value is never used. So really the only thing this code + ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) + */ + /*memset(&yygotominor, 0, sizeof(yygotominor));*/ + yygotominor = yyzerominor; + + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); + if( yyact < YYNSTATE ){ +#ifdef NDEBUG + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if( yysize ){ + yypParser->yyidx++; + yymsp -= yysize-1; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yymsp->minor = yygotominor; + }else +#endif + { + yy_shift(yypParser,yyact,yygoto,&yygotominor); + } + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ + yyminorunion = yyzerominor; + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); + if( yyactyyerrcnt--; + yymajor = YYNOCODE; + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_reduce_action( + yypParser->yystack[yypParser->yyidx].stateno, + YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor,yyminorunion); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yymajor = YYNOCODE; + +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/external/badvpn_dns/lime/HOWTO b/external/badvpn_dns/lime/HOWTO new file mode 100644 index 00000000..f447c84f --- /dev/null +++ b/external/badvpn_dns/lime/HOWTO @@ -0,0 +1,70 @@ +Lime: An LALR(1) parser generator in and for PHP. + +Interpretter pattern got you down? Time to use a real parser? Welcome to Lime. + +If you're familiar with BISON or YACC, you may want to read the metagrammar. +It's written in the Lime input language, so you'll get a head-start on +understanding how to use Lime. + +0. If you're not running Linux on an IA32 box, then you will have to rebuild + lime_scan_tokens for your system. It should be enough to erase it, + and then type "CFLAGS=-O2 make lime_scan_tokens" at the bash prompt. + +1. Stare at the file lime/metagrammar to understand the syntax. You're seeing + slightly modified and tweaked Backus-Naur forms. The main differences + are that you get to name your components, instead of refering to them + by numbers the way that BISON demands. This idea was stolen from the + C-based "Lemon" parser from which Lime derives its name. Incidentally, + the author of Lemon disclaimed copyright, so you get a copy of the C + code that taught me LALR(1) parsing better than any book, despite the + obvious difficulties in understanding it. Oh, and one other thing: + symbols are terminal if the scanner feeds them to the parser. They + are non-terminal if they appear on the left side of a production rule. + Lime names semantic categories using strings instead of the numbers + that BISON-based parsers use, so you don't have to declare any list of + terminal symbols anywhere. + +2. Look at the file lime/lime.php to see what pragmas are defined. To be more + specific, you might look at the method lime::pragma(), which at the + time of this writing, supports "%left", "%right", "%nonassoc", + "%start", and "%class". The first three are for operator precedence. + The last two declare the start symbol and the name of a PHP class to + generate which will hold all the bottom-up parsing tables. + +3. Write a grammar file. + +4. php /path/to/lime/lime.php list-of-grammar-files > my_parser.php + +5. Read the function parse_lime_grammar() in lime.php to understand + how to integrate your parser into your program. + +6. Integrate your parser as follows: + +--------------- CUT --------------- + +include_once "lime/parse_engine.php"; +include_once "my_parser.php"; +# +# Later: +# +$parser = new parse_engine(new my_parser()); +# +# And still later: +# +try { + while (..something..) { + $parser->eat($type, $val); + # You figure out how to get the parameters. + } + # And after the last token has been eaten: + $parser->eat_eof(); +} catch (parse_error $e) { + die($e->getMessage()); +} +return $parser->semantic; + +--------------- CUT --------------- + +7. You now have the computed semantic value of whatever you parsed. Add salt + and pepper to taste, and serve. + diff --git a/external/badvpn_dns/lime/flex_token_stream.php b/external/badvpn_dns/lime/flex_token_stream.php new file mode 100644 index 00000000..c21e411f --- /dev/null +++ b/external/badvpn_dns/lime/flex_token_stream.php @@ -0,0 +1,34 @@ +executable(); + $tokens = explode("\0", `$scanner < "\$PHP_LIME_SCAN_STDIN"`); + array_pop($tokens); + $this->tokens = $tokens; + $this->lineno = 1; + } + function next() { + if (list($key, $token) = each($this->tokens)) { + list($this->lineno, $type, $text) = explode("\1", $token); + return array($type, $text); + } + } + function feed($parser) { + while (list($type, $text) = $this->next()) { + $parser->eat($type, $text); + } + return $parser->eat_eof(); + } +} diff --git a/external/badvpn_dns/lime/lemon.c b/external/badvpn_dns/lime/lemon.c new file mode 100644 index 00000000..708b3538 --- /dev/null +++ b/external/badvpn_dns/lime/lemon.c @@ -0,0 +1,4588 @@ +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include +#include +#include +#include +#include + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +/* #define PRIVATE static */ +#define PRIVATE + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +char *msort(); +extern void *malloc(); + +/******** From the file "action.h" *************************************/ +struct action *Action_new(); +struct action *Action_sort(); + +/********* From the file "assert.h" ************************************/ +void myassert(); +#ifndef NDEBUG +# define assert(X) if(!(X))myassert(__FILE__,__LINE__) +#else +# define assert(X) +#endif + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + char *arg; + char *message; +}; +int OptInit(/* char**,struct s_options*,FILE* */); +int OptNArgs(/* void */); +char *OptArg(/* int */); +void OptErr(/* int */); +void OptPrint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {B_FALSE=0, B_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if predecence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destructorln; /* Line number of destructor code */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + CONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int index; /* Sequencial number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + int includeln; /* Line number for start of include code */ + char *error; /* Code to execute when an error is seen */ + int errorln; /* Line number for start of error code */ + char *overflow; /* Code to execute on a stack overflow */ + int overflowln; /* Line number for start of overflow code */ + char *failure; /* Code to execute on parser failure */ + int failureln; /* Line number for start of failure code */ + char *accept; /* Code to execute when the parser excepts */ + int acceptln; /* Line number for the start of accept code */ + char *extracode; /* Code appended to the generated file */ + int extracodeln; /* Line number for the start of the extra code */ + char *tokendest; /* Code to execute to destroy token data */ + int tokendestln; /* Line number for token destroyer code */ + char *vardest; /* Code for the default non-terminal destructor */ + int vardestln; /* Line number for default non-term destructor code*/ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammer */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + extern void memory_error(); \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +struct action *Action_new(){ + static struct action *freelist = 0; + struct action *new; + + if( freelist==0 ){ + int i; + int amt = 100; + freelist = (struct action *)malloc( sizeof(struct action)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Compare two actions */ +static int actioncmp(ap1,ap2) +struct action *ap1; +struct action *ap2; +{ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ) rc = (int)ap1->type - (int)ap2->type; + if( rc==0 ){ + assert( ap1->type==REDUCE || ap1->type==RD_RESOLVED || ap1->type==CONFLICT); + assert( ap2->type==REDUCE || ap2->type==RD_RESOLVED || ap2->type==CONFLICT); + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +struct action *Action_sort(ap) +struct action *ap; +{ + ap = (struct action *)msort((char *)ap,(char **)&ap->next,actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +char *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure +*/ +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ + } *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(void){ + acttab *p = malloc( sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set +*/ +void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookaheadmxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; inActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset where we can + ** insert the current transaction set. Fall out of the loop when that + ** offset is found. In the worst case, we fall out of the loop when + ** i reaches p->nAction, which means we append the new transaction set. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=0; inAction+p->mnLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( jnLookahead ) continue; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + }else if( p->aAction[i].lookahead==p->mnLookahead ){ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( jnLookahead ) continue; + n = 0; + for(j=0; jnAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* Same as a prior transaction set */ + } + } + } + /* Insert transaction set at index i. */ + for(j=0; jnLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "assert.c" ****************************/ +/* +** A more efficient way of handling assertions. +*/ +void myassert(file,line) +char *file; +int line; +{ + fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file); + exit(1); +} +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->prec>=0 ){ + rp->precsym = rp->rhs[i]; + break; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i; + struct rule *rp; + int progress; + + for(i=0; insymbol; i++){ + lemp->symbols[i]->lambda = B_FALSE; + } + for(i=lemp->nterminal; insymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; inrhs; i++){ + if( rp->rhs[i]->lambda==B_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = B_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; inrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s1==s2 ){ + if( s1->lambda==B_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==B_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; inrhs; i++){ + if( rp->rhs[i]==sp ){ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->index = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( bsp!=sp ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; instate; i++){ + for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; instate; i++){ /* Loop over all states */ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; jnterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],(char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap,ACCEPT,sp,0); + + /* Resolve conflicts */ + for(i=0; instate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + assert( stp->ap ); + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = B_FALSE; + for(i=0; instate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = B_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolve, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = CONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->precprec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==CONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==CONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)malloc( sizeof(struct config)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; inext; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; inrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==B_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = strlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = strlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(){ + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z){ + char **paz; + nDefine++; + azDefine = realloc(azDefine, sizeof(azDefine[0])*nDefine); + if( azDefine==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine-1]; + *paz = malloc( strlen(z)+1 ); + if( *paz==0 ){ + fprintf(stderr,"out of memory\n"); + exit(1); + } + strcpy(*paz, z); + for(z=*paz; *z && *z!='='; z++){} + *z = 0; +} + + +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs()!=1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.has_fallback = 0; + lem.nconflict = 0; + lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0; + lem.vartype = 0; + lem.stacksize = 0; + lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest = + lem.tokenprefix = lem.outname = lem.extracode = 0; + lem.vardest = 0; + lem.tablesize = 0; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.rule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + lem.nsymbol = Symbol_count(); + Symbol_new("{default}"); + lem.symbols = Symbol_arrayof(); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; isupper(lem.symbols[i]->name[0]); i++); + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); + return (lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((unsigned long)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge(a,b,cmp,offset) +char *a; +char *b; +int (*cmp)(); +int offset; +{ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +char *msort(list,next,cmp) +char *list; +char **next; +int (*cmp)(); +{ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; istate = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is not prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)malloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; inrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbol on RHS or rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + psp->decllnslot = &psp->gp->includeln; + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + psp->decllnslot = &psp->gp->extracodeln; + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + psp->decllnslot = &psp->gp->tokendestln; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + psp->decllnslot = &psp->gp->vardestln; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + psp->decllnslot = &psp->gp->errorln; + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + psp->decllnslot = &psp->gp->acceptln; + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + psp->decllnslot = &psp->gp->failureln; + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + psp->decllnslot = &psp->gp->overflowln; + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllnslot = &sp->destructorln; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){ + if( *(psp->declargslot)!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The argument \"%s\" to declaration \"%%%s\" is not the first.", + x[0]=='\"' ? &x[1] : x,psp->declkeyword); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x; + if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno; + psp->state = WAITING_FOR_DECL_OR_RULE; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* Run the proprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static preprocess_input(char *z){ + int i, j, k, n; + int exclude = 0; + int start; + int lineno = 1; + int start_lineno; + for(i=0; z[i]; i++){ + if( z[i]=='\n' ) lineno++; + if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; + if( strncmp(&z[i],"%endif",6)==0 && isspace(z[i+6]) ){ + if( exclude ){ + exclude--; + if( exclude==0 ){ + for(j=start; jfilename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; inext; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( strlen(lemp->filename) + strlen(suffix) + 5 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + strcpy(name,lemp->filename); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; insymbol; i++){ + sp = lemp->symbols[i]; + len = strlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; insymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); +/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; inrhs; i++){ + printf(" %s",rp->rhs[i]->name); +/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); +/* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + int i; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + fprintf(fp," %s",rp->rhs[i]->name); + } +} + +/* #define TEST */ +#ifdef TEST +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case CONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","wb"); + if( fp==0 ) return; + fprintf(fp," \b"); + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->index); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#ifdef TEST + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + extern int access(); + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( strlen(argv0) + strlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + extern char *getenv(); + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( strlen(pathlist)+strlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[strlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->index; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + static char templatename[] = "lempar.c"; + char buf[1000]; + FILE *in; + char *tpltname; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(templatename,004)==0 ){ + tpltname = templatename; + }else{ + tpltname = pathsearch(lemp->argv0,templatename,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"rb"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); + lemp->errorcnt++; + return 0; + } + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(out,lineno,filename) +FILE *out; +int lineno; +char *filename; +{ + fprintf(out,"#line %d \"",lineno); + while( *filename ){ + if( *filename == '\\' ) putc('\\',out); + putc(*filename,out); + filename++; + } + fprintf(out,"\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,strln,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int strln; +int *lineno; +{ + if( str==0 ) return; + tplt_linedir(out,strln,lemp->filename); + (*lineno)++; + while( *str ){ + if( *str=='\n' ) (*lineno)++; + putc(*str,out); + str++; + } + if( str[-1]!='\n' ){ + putc('\n',out); + (*lineno)++; + } + tplt_linedir(out,*lineno+2,lemp->outname); + (*lineno)+=2; + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp = 0; + + int linecnt = 0; + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + tplt_linedir(out,lemp->tokendestln,lemp->filename); + fprintf(out,"{"); + }else if( sp->destructor ){ + cp = sp->destructor; + tplt_linedir(out,sp->destructorln,lemp->filename); + fprintf(out,"{"); + }else if( lemp->vardest ){ + cp = lemp->vardest; + if( cp==0 ) return; + tplt_linedir(out,lemp->vardestln,lemp->filename); + fprintf(out,"{"); + }else{ + assert( 0 ); /* Cannot happen */ + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } + (*lineno) += 3 + linecnt; + fprintf(out,"}\n"); + tplt_linedir(out,*lineno,lemp->outname); + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(char *zText, int n, int p1, int p2){ + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + + if( zText==0 ){ + used = 0; + return z; + } + if( n<=0 ){ + if( n<0 ){ + used += n; + assert( used>=0 ); + } + n = strlen(zText); + } + if( n+sizeof(zInt)*2+used >= alloced ){ + alloced = n + sizeof(zInt)*2 + used + 200; + z = realloc(z, alloced); + } + if( z==0 ) return ""; + while( n-- > 0 ){ + c = *(zText++); + if( c=='%' && zText[0]=='d' ){ + sprintf(zInt, "%d", p1); + p1 = p2; + strcpy(&z[used], zInt); + used += strlen(&z[used]); + zText++; + n--; + }else{ + z[used++] = c; + } + } + z[used] = 0; + return z; +} + +/* +** zCode is a string that is the action associated with a rule. Expand +** the symbols in this string so that the refer to elements of the parser +** stack. +*/ +PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ + char *cp, *xp; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; inrhs; i++) used[i] = 0; + lhsused = 0; + + append_str(0,0,0,0); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + append_str("yygotominor.yy%d",0,rp->lhs->dtnum,0); + cp = xp; + lhsused = 1; + }else{ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + if( cp!=rp->code && cp[-1]=='@' ){ + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); + }else{ + append_str("yymsp[%d].minor.yy%d",0, + i-rp->nrhs+1,rp->rhs[i]->dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; inrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + append_str(" yy_destructor(%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index,i-rp->nrhs+1); + }else{ + /* No destructor defined for this term */ + } + } + } + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp); +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp; + int linecnt = 0; + + /* Generate code to do the reduce action */ + if( rp->code ){ + tplt_linedir(out,rp->line,lemp->filename); + fprintf(out,"{%s",rp->code); + for(cp=rp->code; *cp; cp++){ + if( *cp=='\n' ) linecnt++; + } /* End loop */ + (*lineno) += 3 + linecnt; + fprintf(out,"}\n"); + tplt_linedir(out,*lineno,lemp->outname); + } /* End if( rp->code ) */ + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)malloc( arraysize * sizeof(char*) ); + for(i=0; ivartype ){ + maxdtlength = strlen(lemp->vartype); + } + for(i=0; insymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = strlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + hash = 0; + for(j=0; stddt[j]; j++){ + hash = hash*53 + stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( strlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; ierrsym->dtnum); lineno++; + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + return p2->nAction - p1->nAction; +} + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","wb"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno); + if( mhflag ){ + char *name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+5)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + print_stack_union(out,lemp,&lineno,mhflag); + if( lemp->stacksize ){ + if( atoi(lemp->stacksize)<=0 ){ + ErrorMsg(lemp->filename,0, +"Illegal stack size: [%s]. The stack size should be an integer constant.", + lemp->stacksize); + lemp->errorcnt++; + lemp->stacksize = "100"; + } + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + int i; + i = strlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = malloc( sizeof(ax[0])*lemp->nstate*2 ); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->indexnterminal ){ + stp->nTknAct++; + }else if( ap->sp->indexnsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; instate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfstiTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->indexnterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfstiNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++; + n = acttab_size(pActtab); + for(i=j=0; insymbol + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; insymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + fprintf(out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; isorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + for(i=0; interminal; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; insymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammer. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"%s ::=", i, rp->lhs->name); + for(j=0; jnrhs; j++) fprintf(out," %s",rp->rhs[j]->name); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + } + for(i=0; insymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( insymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + + /* Combine duplicate destructors into a single case */ + for(j=i+1; jnsymbol; j++){ + struct symbol *sp2 = lemp->symbols[j]; + if( sp2 && sp2->type!=TERMINAL && sp2->destructor + && sp2->dtnum==sp->dtnum + && strcmp(sp->destructor,sp2->destructor)==0 ){ + fprintf(out," case %d:\n",sp2->index); lineno++; + sp2->destructor = 0; + } + } + + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + for(i=0; insymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->code ) translate_code(lemp, rp); + } + for(rp=lemp->rule; rp; rp=rp->next){ + struct rule *rp2; + if( rp->code==0 ) continue; + fprintf(out," case %d:\n",rp->index); lineno++; + for(rp2=rp->next; rp2; rp2=rp2->next){ + if( rp2->code==rp->code ){ + fprintf(out," case %d:\n",rp2->index); lineno++; + rp2->code = 0; + } + } + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","rb"); + if( in ){ + for(i=1; interminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","wb"); + if( out ){ + for(i=1; interminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Only default a reduce if there are more than one. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + + for(i=0; instate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 2 */ + if( nbest<2 ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + int i; + s = (char*)malloc( size ); + if( s==0 ){ + extern void memory_error(); + memory_error(); + } + for(i=0; isize = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)malloc( sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = B_FALSE; + sp->destructor = 0; + sp->datatype = 0; + Symbol_insert(sp,sp->name); + } + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(struct symbol **a, struct symbol **b){ + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)malloc( sizeof(struct symbol *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + int h=0; + while( a ){ + h = h*571 + a->rp->index*37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)malloc( sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; itbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; icount; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; icount; i++) (*f)(x4a->tbl[i].data); + for(i=0; isize; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/external/badvpn_dns/lime/lime.bootstrap b/external/badvpn_dns/lime/lime.bootstrap new file mode 100644 index 00000000..63791c71 --- /dev/null +++ b/external/badvpn_dns/lime/lime.bootstrap @@ -0,0 +1,31 @@ +There is nothing to see here. Go and look at the file called "metagrammar". + +: $$ = new lime(); +grammar pragma toklist stop : $$->pragma($2, $3); +grammar rewrite stop : $2->update($$); +to grammar +: {$$=array();} +toklist sym : $$[] = $2; +toklist lit : $$[] = $2; +to toklist +sym '=' rhs : $$ = new lime_rewrite($1); $$->add_rhs($3); +rewrite '|' rhs : $$->add_rhs($3); +to rewrite +list : $$ = new lime_rhs($1, ''); +list action : $$ = new lime_rhs($1, $2); +to rhs +action : $$ = new lime_action($1, NULL); +action lambda : $$ = new lime_action($1, $2); +sym : $$ = new lime_glyph($1, NULL); +sym lambda : $$ = new lime_glyph($1, $2); +lit : $$ = new lime_glyph($1, NULL); +to slot +: $$ = new lime_rhs(); +rhs slot : $$->add($2); +to rhs +'{' code '}' : $$ = $2; +to action +: +code php : $$.=$2; +code '{' code '}' : $$.='{'.$3.'}'; +to code diff --git a/external/badvpn_dns/lime/lime.php b/external/badvpn_dns/lime/lime.php new file mode 100644 index 00000000..b049225e --- /dev/null +++ b/external/badvpn_dns/lime/lime.php @@ -0,0 +1,910 @@ +code=$code; } +} +class step { + /* + Base class for parse table instructions. The main idea is to make the + subclasses responsible for conflict resolution among themselves. It also + forms a sort of interface to the parse table. + */ + function __construct($sym) { + bug_unless($sym instanceof sym); + $this->sym = $sym; + } + function glyph() { return $this->sym->name; } +} +class error extends step { + function sane() { return false; } + function instruction() { bug("This should not happen."); } + function decide($that) { return $this; /* An error shall remain one. */ } +} +class shift extends step { + function __construct($sym, $q) { + parent::__construct($sym); + $this->q = $q; + } + function sane() { return true; } + function instruction() { return "s $this->q"; } + function decide($that) { + # shift-shift conflicts are impossible. + # shift-accept conflicts are a bug. + # so we can infer: + bug_unless($that instanceof reduce); + + # That being said, the resolution is a matter of precedence. + $shift_prec = $this->sym->right_prec; + $reduce_prec = $that->rule->prec; + + # If we don't have defined precedence levels for both options, + # then we default to shifting: + if (!($shift_prec and $reduce_prec)) return $this; + + # Otherwise, use the step with higher precedence. + if ($shift_prec > $reduce_prec) return $this; + if ($reduce_prec > $shift_prec) return $that; + + # The "nonassoc" works by giving equal precedence to both options, + # which means to put an error instruction in the parse table. + return new error($this->sym); + } +} +class reduce extends step { + function __construct($sym, $rule) { + bug_unless($rule instanceof rule); + parent::__construct($sym); + $this->rule = $rule; + } + function sane() { return true; } + function instruction() { return 'r '.$this->rule->id; } + function decide($that) { + # This means that the input grammar has a reduce-reduce conflict. + # Such things are considered an error in the input. + throw new RRC($this, $that); + #exit(1); + # BISON would go with the first encountered reduce thus: + # return $this; + } +} +class accept extends step { + function __construct($sym) { parent::__construct($sym); } + function sane() { return true; } + function instruction() { return 'a '.$this->sym->name; } +} +class RRC extends Exception { + function __construct($a, $b) { + parent::__construct("Reduce-Reduce Conflict"); + $this->a = $a; + $this->b = $b; + } + function make_noise() { + emit(sprintf( + "Reduce-Reduce Conflict:\n%s\n%s\nLookahead is (%s)", + $this->a->rule->text(), + $this->b->rule->text(), + $this->a->glyph() + )); + } +} +class state { + function __construct($id, $key, $close) { + $this->id = $id; + $this->key = $key; + $this->close = $close; # config key -> object + ksort($this->close); + $this->action = array(); + } + function dump() { + echo " * ".$this->id.' / '.$this->key."\n"; + foreach ($this->close as $config) $config->dump(); + } + function add_shift($sym, $state) { + $this->add_instruction(new shift($sym, $state->id)); + } + function add_reduce($sym, $rule) { + $this->add_instruction(new reduce($sym, $rule)); + } + function add_accept($sym) { + $this->add_instruction(new accept($sym)); + } + function add_instruction($step) { + bug_unless($step instanceof step); + $this->action[] = $step; + } + function find_reductions($lime) { + # rightmost configurations followset yields reduce. + foreach($this->close as $c) { + if ($c->rightmost) { + foreach ($c->follow->all() as $glyph) $this->add_reduce($lime->sym($glyph), $c->rule); + } + } + } + function resolve_conflicts() { + # For each possible lookahead, find one (and only one) step to take. + $table = array(); + foreach ($this->action as $step) { + $glyph = $step->glyph(); + if (isset($table[$glyph])) { + # There's a conflict. The shifts all came first, which + # simplifies the coding for the step->decide() methods. + try { + $table[$glyph] = $table[$glyph]->decide($step); + } catch (RRC $e) { + emit("State $this->id:"); + $e->make_noise(); + } + } else { + # This glyph is yet unprocessed, so the step at hand is + # our best current guess at what the grammar indicates. + $table[$glyph] = $step; + } + } + + # Now that we have the correct steps chosen, this routine is oddly + # also responsible for turning that table into the form that will + # eventually be passed to the parse engine. (So FIXME?) + $out = array(); + foreach ($table as $glyph => $step) { + if ($step->sane()) $out[$glyph] = $step->instruction(); + } + return $out; + } + function segment_config() { + # Filter $this->close into categories based on the symbol_after_the_dot. + $f = array(); + foreach ($this->close as $c) { + $p = $c->symbol_after_the_dot; + if (!$p) continue; + $f[$p->name][] = $c; + } + return $f; + } +} +class sym { + function __construct($name, $id) { + $this->name=$name; + $this->id=$id; + $this->term = true; # Until proven otherwise. + $this->rule = array(); + $this->config = array(); + $this->lambda = false; + $this->first = new set(); + $this->left_prec = $this->right_prec = 0; + } + function summary() { + $out = ''; + foreach ($this->rule as $rule) $out .= $rule->text()."\n"; + return $out; + } +} +class rule { + function __construct($id, $sym, $rhs, $code, $look, $replace) { + $this->id = $id; + $this->sym = $sym; + $this->rhs = $rhs; + $this->code = $code; + $this->look = $look; + bug_unless(is_int($look)); + $this->replace = $replace; + #$this->prec_sym = $prec_sym; + $this->prec = 0; + $this->first = array(); + $this->epsilon = count($rhs); + } + function lhs_glyph() { return $this->sym->name; } + function determine_precedence() { + # We may eventually expand to allow explicit prec_symbol declarations. + # Until then, we'll go with the rightmost terminal, which is what + # BISON does. People probably expect that. The leftmost terminal + # is a reasonable alternative behaviour, but I don't see the big + # deal just now. + + #$prec_sym = $this->prec_sym; + #if (!$prec_sym) + $prec_sym = $this->rightmost_terminal(); + if (!$prec_sym) return; + $this->prec = $prec_sym->left_prec; + } + private function rightmost_terminal() { + $symbol = NULL; + $rhs = $this->rhs; + while ($rhs) { + $symbol = array_pop($rhs); + if ($symbol->term) break; + } + return $symbol; + } + function text() { + $t = "($this->id) ".$this->lhs_glyph().' :='; + foreach($this->rhs as $s) $t .= ' '.$s->name; + return $t; + } + function table(lime_language $lang) { + return array( + 'symbol' => $this->lhs_glyph(), + 'len' => $this->look, + 'replace' => $this->replace, + 'code' => $lang->fixup($this->code), + 'text' => $this->text(), + ); + } + function lambda() { + foreach ($this->rhs as $sym) if (!$sym->lambda) return false; + return true; + } + function find_first() { + $dot = count($this->rhs); + $last = $this->first[$dot] = new set(); + while ($dot) { + $dot--; + $symbol_after_the_dot = $this->rhs[$dot]; + $first = $symbol_after_the_dot->first->all(); + bug_if(empty($first) and !$symbol_after_the_dot->lambda); + $set = new set($first); + if ($symbol_after_the_dot->lambda) { + $set->union($last); + if ($this->epsilon == $dot+1) $this->epsilon = $dot; + } + $last = $this->first[$dot] = $set; + } + } + function teach_symbol_of_first_set() { + $go = false; + foreach ($this->rhs as $sym) { + if ($this->sym->first->union($sym->first)) $go = true; + if (!$sym->lambda) break; + } + return $go; + } + function lambda_from($dot) { + return $this->epsilon <= $dot; + } + function leftmost($follow) { + return new config($this, 0, $follow); + } + function dotted_text($dot) { + $out = $this->lhs_glyph().' :='; + $idx = -1; + foreach($this->rhs as $idx => $s) { + if ($idx == $dot) $out .= ' .'; + $out .= ' '.$s->name; + } + if ($dot > $idx) $out .= ' .'; + return $out; + } +} +class config { + function __construct($rule, $dot, $follow) { + $this->rule=$rule; + $this->dot = $dot; + $this->key = "$rule->id.$dot"; + $this->rightmost = count($rule->rhs) <= $dot; + $this->symbol_after_the_dot = $this->rightmost ? null : $rule->rhs[$dot]; + $this->_blink = array(); + $this->follow = new set($follow); + $this->_flink= array(); + bug_unless($this->rightmost or count($rule)); + } + function text() { + $out = $this->rule->dotted_text($this->dot); + $out .= ' [ '.implode(' ', $this->follow->all()).' ]'; + return $out; + } + function blink($config) { + $this->_blink[] = $config; + } + function next() { + bug_if($this->rightmost); + $c = new config($this->rule, $this->dot+1, array()); + # Anything in the follow set for this config will also be in the next. + # However, we link it backwards because we might wind up selecting a + # pre-existing state, and the housekeeping is easier in the first half + # of the program. We'll fix it before doing the propagation. + $c->blink($this); + return $c; + } + function copy_links_from($that) { + foreach($that->_blink as $c) $this->blink($c); + } + function lambda() { + return $this->rule->lambda_from($this->dot); + } + function simple_follow() { + return $this->rule->first[$this->dot+1]->all(); + } + function epsilon_follows() { + return $this->rule->lambda_from($this->dot+1); + } + function fixlinks() { + foreach ($this->_blink as $that) $that->_flink[] = $this; + $this->blink = array(); + } + function dump() { + echo " * "; + echo $this->key.' : '; + echo $this->rule->dotted_text($this->dot); + echo $this->follow->text(); + foreach ($this->_flink as $c) echo $c->key.' / '; + echo "\n"; + } +} +class lime { + var $parser_class = 'parser'; + function __construct() { + $this->p_next = 1; + $this->sym = array(); + $this->rule = array(); + $this->start_symbol_set = array(); + $this->state = array(); + $this->stop = $this->sym('#'); + #$err = $this->sym('error'); + $err->term = false; + $this->lang = new lime_language_php(); + } + function language() { return $this->lang; } + function build_parser() { + $this->add_start_rule(); + foreach ($this->rule as $r) $r->determine_precedence(); + $this->find_sym_lamdba(); + $this->find_sym_first(); + foreach ($this->rule as $rule) $rule->find_first(); + $initial = $this->find_states(); + $this->fixlinks(); + # $this->dump_configurations(); + $this->find_follow_sets(); + foreach($this->state as $s) $s->find_reductions($this); + $i = $this->resolve_conflicts(); + $a = $this->rule_table(); + $qi = $initial->id; + return $this->lang->ptab_to_class($this->parser_class, compact('a', 'qi', 'i')); + } + function rule_table() { + $s = array(); + foreach ($this->rule as $i => $r) { + $s[$i] = $r->table($this->lang); + } + return $s; + } + function add_rule($symbol, $rhs, $code) { + $this->add_raw_rule($symbol, $rhs, $code, count($rhs), true); + } + function trump_up_bogus_lhs($real) { + return "'$real'".count($this->rule); + } + function add_raw_rule($lhs, $rhs, $code, $look, $replace) { + $sym = $this->sym($lhs); + $sym->term=false; + if (empty($rhs)) $sym->lambda = true; + $rs = array(); + foreach ($rhs as $str) $rs[] = $this->sym($str); + $rid = count($this->rule); + $r = new rule($rid, $sym, $rs, $code, $look, $replace); + $this->rule[$rid] = $r; + $sym->rule[] = $r; + } + function sym($str) { + if (!isset($this->sym[$str])) $this->sym[$str] = new sym($str, count($this->sym)); + return $this->sym[$str]; + } + function summary() { + $out = ''; + foreach ($this->sym as $sym) if (!$sym->term) $out .= $sym->summary(); + return $out; + } + private function find_sym_lamdba() { + do { + $go = false; + foreach ($this->sym as $sym) if (!$sym->lambda) { + foreach ($sym->rule as $rule) if ($rule->lambda()) { + $go = true; + $sym->lambda = true; + } + } + } while ($go); + } + private function teach_terminals_first_set() { + foreach ($this->sym as $sym) if ($sym->term) $sym->first->add($sym->name); + } + private function find_sym_first() { + $this->teach_terminals_first_set(); + do { + $go = false; + foreach ($this->rule as $r) if ($r->teach_symbol_of_first_set()) $go = true; + } while ($go); + } + function add_start_rule() { + $rewrite = new lime_rewrite("'start'"); + $rhs = new lime_rhs(); + $rhs->add(new lime_glyph($this->deduce_start_symbol()->name, NULL)); + #$rhs->add(new lime_glyph($this->stop->name, NULL)); + $rewrite->add_rhs($rhs); + $rewrite->update($this); + } + private function deduce_start_symbol() { + $candidate = current($this->start_symbol_set); + # Did the person try to set a start symbol at all? + if (!$candidate) return $this->first_rule_lhs(); + # Do we actually have such a symbol on the left of a rule? + if ($candidate->terminal) return $this->first_rule_lhs(); + # Ok, it's a decent choice. We need to return the symbol entry. + return $this->sym($candidate); + } + private function first_rule_lhs() { + reset($this->rule); + $r = current($this->rule); + return $r->sym; + } + function find_states() { + /* + Build an initial state. This is a recursive process which digs out + the LR(0) state graph. + */ + $start_glyph = "'start'"; + $sym = $this->sym($start_glyph); + $basis = array(); + foreach($sym->rule as $rule) { + $c = $rule->leftmost(array('#')); + $basis[$c->key] = $c; + } + $initial = $this->get_state($basis); + $initial->add_accept($sym); + return $initial; + } + function get_state($basis) { + $key = array_keys($basis); + sort($key); + $key = implode(' ', $key); + if (isset($this->state[$key])) { + # Copy all the links around... + $state = $this->state[$key]; + foreach($basis as $config) $state->close[$config->key]->copy_links_from($config); + return $state; + } else { + $close = $this->state_closure($basis); + $this->state[$key] = $state = new state(count($this->state), $key, $close); + $this->build_shifts($state); + return $state; + } + } + private function state_closure($q) { + # $q is a list of config. + $close = array(); + while ($config = array_pop($q)) { + if (isset($close[$config->key])) { + $close[$config->key]->copy_links_from($config); + $close[$config->key]->follow->union($config->follow); + continue; + } + $close[$config->key] = $config; + + $symbol_after_the_dot = $config->symbol_after_the_dot; + if (!$symbol_after_the_dot) continue; + + if (! $symbol_after_the_dot->term) { + foreach ($symbol_after_the_dot->rule as $r) { + $station = $r->leftmost($config->simple_follow()); + if ($config->epsilon_follows()) $station->blink($config); + $q[] = $station; + } + # The following turned out to be wrong. Don't do it. + #if ($symbol_after_the_dot->lambda) { + # $q[] = $config->next(); + #} + } + + } + return $close; + } + function build_shifts($state) { + foreach ($state->segment_config() as $glyph => $segment) { + $basis = array(); + foreach ($segment as $preshift) { + $postshift = $preshift->next(); + $basis[$postshift->key] = $postshift; + } + $dest = $this->get_state($basis); + $state->add_shift($this->sym($glyph), $dest); + } + } + function fixlinks() { + foreach ($this->state as $s) foreach ($s->close as $c) $c->fixlinks(); + } + function find_follow_sets() { + $q = array(); + foreach ($this->state as $s) foreach ($s->close as $c) $q[] = $c; + while ($q) { + $c = array_shift($q); + foreach ($c->_flink as $d) { + if ($d->follow->union($c->follow)) $q[] = $d; + } + } + } + private function set_assoc($ss, $l, $r) { + $p = ($this->p_next++)*2; + foreach ($ss as $glyph) { + $s = $this->sym($glyph); + $s->left_prec = $p+$l; + $s->right_prec = $p+$r; + } + } + function left_assoc($ss) { $this->set_assoc($ss, 1, 0); } + function right_assoc($ss) { $this->set_assoc($ss, 0, 1); } + function non_assoc($ss) { $this->set_assoc($ss, 0, 0); } + private function resolve_conflicts() { + # For each state, try to find one and only one + # thing to do for any given lookahead. + $i = array(); + foreach ($this->state as $s) $i[$s->id] = $s->resolve_conflicts(); + return $i; + } + function dump_configurations() { + foreach ($this->state as $q) $q->dump(); + } + function dump_first_sets() { + foreach ($this->sym as $s) { + echo " * "; + echo $s->name.' : '; + echo $s->first->text(); + echo "\n"; + } + } + function add_rule_with_actions($lhs, $rhs) { + # First, make sure this thing is well-formed. + if(!is_object(end($rhs))) $rhs[] = new cf_action(''); + # Now, split it into chunks based on the actions. + $look = -1; + $subrule = array(); + $subsymbol = ''; + while (count($rhs)) { + $it = array_shift($rhs); + $look ++; + if (is_string($it)) { + $subrule[] = $it; + } else { + $code = $it->code; + # It's an action. + # Is it the last one? + if (count($rhs)) { + # no. + $subsymbol = $this->trump_up_bogus_lhs($lhs); + $this->add_raw_rule($subsymbol, $subrule, $code, $look, false); + $subrule = array($subsymbol); + } else { + # yes. + $this->add_raw_rule($lhs, $subrule, $code, $look, true); + } + } + } + } + function pragma($type, $args) { + switch ($type) { + case 'left': + $this->left_assoc($args); + break; + + case 'right': + $this->right_assoc($args); + break; + + case 'nonassoc': + $this->non_assoc($args); + break; + + case 'start': + $this->start_symbol_set = $args; + break; + + case 'class': + $this->parser_class = $args[0]; + break; + + default: + emit(sprintf("Bad Parser Pragma: (%s)", $type)); + exit(1); + } + } +} +class lime_language {} +class lime_language_php extends lime_language { + private function result_code($expr) { return "\$result = $expr;\n"; } + function default_result() { return $this->result_code('reset($tokens)'); } + function result_pos($pos) { return $this->result_code(lime_token_reference($pos)); } + function bind($name, $pos) { return "\$$name =& \$tokens[$pos];\n"; } + function fixup($code) { + $code = preg_replace_callback('/\\$(\d+)/', 'lime_token_reference_callback', $code); + $code = preg_replace('/\\$\\$/', '$result', $code); + return $code; + } + function to_php($code) { + return $code; + } + function ptab_to_class($parser_class, $ptab) { + $code = "class $parser_class extends lime_parser {\n"; + $code .= 'var $qi = '.var_export($ptab['qi'], true).";\n"; + $code .= 'var $i = '.var_export($ptab['i'], true).";\n"; + + + $rc = array(); + $method = array(); + $rules = array(); + foreach($ptab['a'] as $k => $a) { + $symbol = preg_replace('/[^\w]/', '', $a['symbol']); + $rn = ++$rc[$symbol]; + $mn = "reduce_${k}_${symbol}_${rn}"; + $method[$k] = $mn; + $comment = "#\n# $a[text]\n#\n"; + $php = $this->to_php($a['code']); + $code .= "function $mn(".LIME_CALL_PROTOCOL.") {\n$comment$php\n}\n\n"; + + + unset($a['code']); + unset($a['text']); + $rules[$k] = $a; + } + + $code .= 'var $method = '.var_export($method, true).";\n"; + $code .= 'var $a = '.var_export($rules, true).";\n"; + + + + $code .= "}\n"; + #echo $code; + return $code; + } +} +class lime_rhs { + function __construct() { + /** + Construct and add glyphs and actions in whatever order. + Then, add this to a lime_rewrite. + + Don't call install_rule. + The rewrite will do that for you when you "update" with it. + */ + $this->rhs = array(); + } + function add($slot) { + bug_unless($slot instanceof lime_slot); + $this->rhs[] = $slot; + } + function install_rule(lime $lime, $lhs) { + # This is the part that has to break the rule into subrules if necessary. + $rhs = $this->rhs; + # First, make sure this thing is well-formed. + if (!(end($rhs) instanceof lime_action)) $rhs[] = new lime_action('', NULL); + # Now, split it into chunks based on the actions. + + $lang = $lime->language(); + $result_code = $lang->default_result(); + $look = -1; + $subrule = array(); + $subsymbol = ''; + $preamble = ''; + while (count($rhs)) { + $it = array_shift($rhs); + $look ++; + if ($it instanceof lime_glyph) { + $subrule[] = $it->data; + } elseif ($it instanceof lime_action) { + $code = $it->data; + # It's an action. + # Is it the last one? + if (count($rhs)) { + # no. + $subsymbol = $lime->trump_up_bogus_lhs($lhs); + $action = $lang->default_result().$preamble.$code; + $lime->add_raw_rule($subsymbol, $subrule, $action, $look, false); + $subrule = array($subsymbol); + } else { + # yes. + $action = $result_code.$preamble.$code; + $lime->add_raw_rule($lhs, $subrule, $action, $look, true); + } + } else { + impossible(); + } + if ($it->name == '$') $result_code = $lang->result_pos($look); + elseif ($it->name) $preamble .= $lang->bind($it->name, $look); + } + } +} +class lime_rewrite { + function __construct($glyph) { + /** + Construct one of these with the name of the lhs. + Add some rhs-es to it. + Finally, "update" the lime you're building. + */ + $this->glyph = $glyph; + $this->rhs = array(); + } + function add_rhs($rhs) { + bug_unless($rhs instanceof lime_rhs); + $this->rhs[] = $rhs; + } + function update(lime $lime) { + foreach ($this->rhs as $rhs) { + $rhs->install_rule($lime, $this->glyph); + + } + } +} +class lime_slot { + /** + This keeps track of one position in an rhs. + We specialize to handle actions and glyphs. + If there is a name for the slot, we store it here. + Later on, this structure will be consulted in the formation of + actual production rules. + */ + function __construct($data, $name) { + $this->data = $data; + $this->name = $name; + } + function preamble($pos) { + if (strlen($this->name) > 0) { + return "\$$this->name =& \$tokens[$pos];\n"; + } + } +} +class lime_glyph extends lime_slot {} +class lime_action extends lime_slot {} +function lime_bootstrap() { + + /* + + This function isn't too terribly interesting to the casual observer. + You're probably better off looking at parse_lime_grammar() instead. + + Ok, if you insist, I'll explain. + + The input to Lime is a CFG parser definition. That definition is + written in some language. (The Lime language, to be exact.) + Anyway, I have to parse the Lime language and compile it into a + very complex data structure from which a parser is eventually + built. What better way than to use Lime itself to parse its own + language? Well, it's almost that simple, but not quite. + + The Lime language is fairly potent, but a restricted subset of + its features was used to write a metagrammar. Then, I hand-translated + that metagrammar into another form which is easy to snarf up. + In the process of reading that simplified form, this function + builds the same sort of data structure that later gets turned into + a parser. The last step is to run the parser generation algorithm, + eval() the resulting PHP code, and voila! With no hard work, I can + suddenly read and comprehend the full range of the Lime language + without ever having written an algorithm to do so. It feels like magic. + + */ + + $bootstrap = LIME_DIR."/lime.bootstrap"; + $lime = new lime(); + $lime->parser_class = 'lime_metaparser'; + $rhs = array(); + bug_unless(is_readable($bootstrap)); + foreach(file($bootstrap) as $l) { + $a = explode(":", $l, 2); + if (count($a) == 2) { + list($pattern, $code) = $a; + $sl = new lime_rhs(); + $pattern = trim($pattern); + if (strlen($pattern)>0) { + foreach (explode(' ', $pattern) as $glyph) $sl->add(new lime_glyph($glyph, NULL)); + } + $sl->add(new lime_action($code, NULL)); + $rhs[] = $sl; + } else { + $m = preg_match('/^to (\w+)$/', $l, $r); + if ($m == 0) continue; + $g = $r[1]; + $rw = new lime_rewrite($g); + foreach($rhs as $b) $rw->add_rhs($b); + $rw->update($lime); + $rhs = array(); + } + } + $parser_code = $lime->build_parser(); + eval($parser_code); +} + +class voodoo_scanner extends flex_scanner { + /* + + The voodoo is in the way I do lexical processing on grammar definition + files. They contain embedded bits of PHP, and it's important to keep + track of things like strings, comments, and matched braces. It seemed + like an ideal problem to solve with GNU flex, so I wrote a little + scanner in flex and C to dig out the tokens for me. Of course, I need + the tokens in PHP, so I designed a simple binary wrapper for them which + also contains line-number information, guaranteed to help out if you + write a grammar which surprises the parser in any manner. + + */ + function executable() { return LIME_DIR.'/lime_scan_tokens'; } +} + +function parse_lime_grammar($path) { + /* + + This is a good function to read because it teaches you how to interface + with a Lime parser. I've tried to isolate out the bits that aren't + instructive in that regard. + + */ + if (!class_exists('lime_metaparser')) lime_bootstrap(); + + $parse_engine = new parse_engine(new lime_metaparser()); + $scanner = new voodoo_scanner($path); + try { + # The result of parsing a Lime grammar is a Lime object. + $lime = $scanner->feed($parse_engine); + # Calling its build_parser() method gets the output PHP code. + return $lime->build_parser(); + } catch (parse_error $e) { + die ($e->getMessage()." in $path line $scanner->lineno.\n"); + } +} + + +if ($_SERVER['argv']) { + $code = ''; + array_shift($_SERVER['argv']); # Strip out the program name. + foreach ($_SERVER['argv'] as $path) { + $code .= parse_lime_grammar($path); + } + + echo " + +/* + +DON'T EDIT THIS FILE! + +This file was automatically generated by the Lime parser generator. +The real source code you should be looking at is in one or more +grammar files in the Lime format. + +THE ONLY REASON TO LOOK AT THIS FILE is to see where in the grammar +file that your error happened, because there are enough comments to +help you debug your grammar. + +If you ignore this warning, you're shooting yourself in the brain, +not the foot. + +*/ + +"{" { + lit(); + yy_push_state(code); +} + +. lit(); + + +{ +\n { + out("stop", "."); + yy_pop_state(); +} +[[:space:]] {} +{SYM} tok("sym"); +{LIT} tok("lit"); +. lit(); +} + +{ +"}" { + lit(); + yy_pop_state(); +} +'{SCHAR}*' php(); +\"{DCHAR}*\" php(); +{COM}.* php(); +{BLOCKCMT} php(); +[^{}'"#/]+ php(); +. php(); +} + +%% + +void lit() { + char lit[] = "'.'"; + lit[1] = *yytext; + out(lit, yytext); +} + +void tok(char*t) { + out(t, yytext); +} + +void php() { + out("php", yytext); +} + +void out(char*type, char*value) { + printf("%d\001%s\001%s", yylineno, type, value); + fputc(0, stdout); +} diff --git a/external/badvpn_dns/lime/metagrammar b/external/badvpn_dns/lime/metagrammar new file mode 100644 index 00000000..5d057c03 --- /dev/null +++ b/external/badvpn_dns/lime/metagrammar @@ -0,0 +1,58 @@ +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# This is the grammar for all other grammar files that will work with the +# Lime LALR(1) Context-Free Grammar Parser Generator. +# You can read this to get an idea how things work, but this file is not +# actually used in the system. Rather, it's an implementation guide for the +# file "lime.bootstrap". + +%class lime_metaparser +%start grammar + +grammar += {$$ = new lime();} +| grammar/$ pragma/p toklist/t stop {$$->pragma($p, $t);} +| grammar/$ rewrite/r stop {$r->update($$);} +. + +rewrite += sym/s '=' rhs/r {$$ = new lime_rewrite($s); $$->add_rhs($r);} +| rewrite/$ '|' rhs/r {$$->add_rhs($r);} +. + +slot += action/a {$$ = new lime_action($a, NULL);} +| action/a lambda/l {$$ = new lime_action($a, $l);} +| sym/s {$$ = new lime_glyph($s, NULL);} +| sym/s lambda/l {$$ = new lime_glyph($s, $l);} +| lit/l {$$ = new lime_glyph($l, NULL);} +. + +rhs += {$$ = new lime_rhs();} +| rhs/$ slot/s {$$->add($s);} +. + +action = '{' code/$ '}' . + +toklist = {$$=array();} +| toklist/$ sym/s {$$[] = $s;} +| toklist/$ lit/l {$$[] = $l;} +. + +code = {} +| code/$ php/p {$$.=$p;} +| code/$ '{' code/c '}' {$$.='{'.$c.'}';} +. diff --git a/external/badvpn_dns/lime/parse_engine.php b/external/badvpn_dns/lime/parse_engine.php new file mode 100644 index 00000000..fd54cc46 --- /dev/null +++ b/external/badvpn_dns/lime/parse_engine.php @@ -0,0 +1,252 @@ +type = $type; + $this->state = $state; + } +} +class parse_premature_eof extends parse_error { + function __construct() { + parent::__construct("Premature EOF"); + } +} + + +class parse_stack { + function __construct($qi) { + $this->q = $qi; + $this->qs = array(); + $this->ss = array(); + } + function shift($q, $semantic) { + $this->ss[] = $semantic; + $this->qs[] = $this->q; + $this->q = $q; + # echo "Shift $q -- $semantic
\n"; + } + function top_n($n) { + if (!$n) return array(); + return array_slice($this->ss, 0-$n); + } + function pop_n($n) { + if (!$n) return array(); + $qq = array_splice($this->qs, 0-$n); + $this->q = $qq[0]; + return array_splice($this->ss, 0-$n); + } + function occupied() { return !empty($this->ss); } + function index($n) { + if ($n) $this->q = $this->qs[count($this->qs)-$n]; + } + function text() { + return $this->q." : ".implode(' . ', array_reverse($this->qs)); + } +} +class parse_engine { + function __construct($parser) { + $this->parser = $parser; + $this->qi = $parser->qi; + $this->rule = $parser->a; + $this->step = $parser->i; + #$this->prepare_callables(); + $this->reset(); + #$this->debug = false; + } + function reset() { + $this->accept = false; + $this->stack = new parse_stack($this->qi); + } + private function enter_error_tolerant_state() { + while ($this->stack->occupied()) { + if ($this->has_step_for('error')) return true; + $this->drop(); + }; + return false; + } + private function drop() { $this->stack->pop_n(1); } + function eat_eof() { + {/* + + So that I don't get any brilliant misguided ideas: + + The "accept" step happens when we try to eat a start symbol. + That happens because the reductions up the stack at the end + finally (and symetrically) tell the parser to eat a symbol + representing what they've just shifted off the end of the stack + and reduced. However, that doesn't put the parser into any + special different state. Therefore, it's back at the start + state. + + That being said, the parser is ready to reduce an EOF to the + empty program, if given a grammar that allows them. + + So anyway, if you literally tell the parser to eat an EOF + symbol, then after it's done reducing and accepting the prior + program, it's going to think it has another symbol to deal with. + That is the EOF symbol, which means to reduce the empty program, + accept it, and then continue trying to eat the terminal EOF. + + This infinte loop quickly runs out of memory. + + That's why the real EOF algorithm doesn't try to pretend that + EOF is a terminal. Like the invented start symbol, it's special. + + Instead, we pretend to want to eat EOF, but never actually + try to get it into the parse stack. (It won't fit.) In short, + we look up what reduction is indicated at each step in the + process of rolling up the parse stack. + + The repetition is because one reduction is not guaranteed to + cascade into another and clean up the entire parse stack. + Rather, it will instead shift each partial production as it + is forced to completion by the EOF lookahead. + */} + + # We must reduce as if having read the EOF symbol + do { + # and we have to try at least once, because if nothing + # has ever been shifted, then the stack will be empty + # at the start. + list($opcode, $operand) = $this->step_for('#'); + switch ($opcode) { + case 'r': $this->reduce($operand); break; + case 'e': $this->premature_eof(); break; + default: throw new parse_bug(); break; + } + } while ($this->stack->occupied()); + {/* + If the sentence is well-formed according to the grammar, then + this will eventually result in eating a start symbol, which + causes the "accept" instruction to fire. Otherwise, the + step('#') method will indicate an error in the syntax, which + here means a premature EOF. + + Incedentally, some tremendous amount of voodoo with the parse + stack might help find the beginning of some unfinished + production that the sentence was cut off during, but as a + general rule that would require deeper knowledge. + */} + if (!$this->accept) throw new parse_bug(); + return $this->semantic; + } + private function premature_eof() { + $seen = array(); + while ($this->enter_error_tolerant_state()) { + if (isset($seen[$this->state()])) { + // This means that it's pointless to try here. + // We're guaranteed that the stack is occupied. + $this->drop(); + continue; + } + $seen[$this->state()] = true; + + $this->eat('error', NULL); + if ($this->has_step_for('#')) { + // Good. We can continue as normal. + return; + } else { + // That attempt to resolve the error condition + // did not work. There's no point trying to + // figure out how much to slice off the stack. + // The rest of the algorithm will make it happen. + } + } + throw new parse_premature_eof(); + } + private function current_row() { return $this->step[$this->state()]; } + private function step_for($type) { + $row = $this->current_row(); + if (!isset($row[$type])) return array('e', $this->stack->q); + return explode(' ', $row[$type]); + } + private function has_step_for($type) { + $row = $this->current_row(); + return isset($row[$type]); + } + private function state() { return $this->stack->q; } + function eat($type, $semantic) { + # assert('$type == trim($type)'); + # if ($this->debug) echo "Trying to eat a ($type)\n"; + list($opcode, $operand) = $this->step_for($type); + switch ($opcode) { + case 's': + # if ($this->debug) echo "shift $type to state $operand\n"; + $this->stack->shift($operand, $semantic); + # echo $this->stack->text()." shift $type
\n"; + break; + + case 'r': + $this->reduce($operand); + $this->eat($type, $semantic); + # Yes, this is tail-recursive. It's also the simplest way. + break; + + case 'a': + if ($this->stack->occupied()) throw new parse_bug('Accept should happen with empty stack.'); + $this->accept = true; + #if ($this->debug) echo ("Accept\n\n"); + $this->semantic = $semantic; + break; + + case 'e': + # This is thought to be the uncommon, exceptional path, so + # it's OK that this algorithm will cause the stack to + # flutter while the parse engine waits for an edible token. + # if ($this->debug) echo "($type) causes a problem.\n"; + if ($this->enter_error_tolerant_state()) { + $this->eat('error', NULL); + if ($this->has_step_for($type)) $this->eat($type, $semantic); + } else { + # If that didn't work, give up: + throw new parse_error("Parse Error: ($type)($semantic) not expected"); + } + break; + + default: + throw new parse_bug("Bad parse table instruction ".htmlspecialchars($opcode)); + } + } + private function reduce($rule_id) { + $rule = $this->rule[$rule_id]; + $len = $rule['len']; + $semantic = $this->perform_action($rule_id, $this->stack->top_n($len)); + #echo $semantic.br(); + if ($rule['replace']) $this->stack->pop_n($len); + else $this->stack->index($len); + $this->eat($rule['symbol'], $semantic); + } + private function perform_action($rule_id, $slice) { + # we have this weird calling convention.... + $result = null; + $method = $this->parser->method[$rule_id]; + #if ($this->debug) echo "rule $id: $method\n"; + $this->parser->$method($slice, $result); + return $result; + } +} diff --git a/external/badvpn_dns/lime/set.so.php b/external/badvpn_dns/lime/set.so.php new file mode 100644 index 00000000..ef87c6cd --- /dev/null +++ b/external/badvpn_dns/lime/set.so.php @@ -0,0 +1,29 @@ +data = array_count_values($list); } + function has($item) { return isset($this->data[$item]); } + function add($item) { $this->data[$item] = true; } + function del($item) { unset($this->data[$item]); return $item;} + function all() { return array_keys($this->data); } + function one() { return key($this->data); } + function count() { return count($this->data); } + function pop() { return $this->del($this->one()); } + function union($that) { + $progress = false; + foreach ($that->all() as $item) if (!$this->has($item)) { + $this->add($item); + $progress = true; + } + return $progress; + } + function text() { + return ' { '.implode(' ', $this->all()).' } '; + } +} diff --git a/external/badvpn_dns/lwip/CHANGELOG b/external/badvpn_dns/lwip/CHANGELOG new file mode 100644 index 00000000..685b5687 --- /dev/null +++ b/external/badvpn_dns/lwip/CHANGELOG @@ -0,0 +1,3396 @@ +HISTORY + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ New features: + + 2012-03-25: Simon Goldschmidt (idea by Mason) + * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h + which are a simple wrapper to the correct lwIP include files. + + 2012-01-16: Simon Goldschmidt + * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP + + 2011-12-17: Simon Goldschmidt + * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW) + (fixes bug #35061) + + 2011-09-27: Simon Goldschmidt + * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989) + (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h) + + 2011-09-21: Simon Goldschmidt + * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on + send (TCP only, bug #33820) + + 2011-09-21: Simon Goldschmidt + * init.c: Converted runtime-sanity-checks into compile-time checks that can + be disabled (since runtime checks can often not be seen on embedded targets) + + 2011-09-11: Simon Goldschmidt + * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file + to get a clear separation of which functions an application or port may use + (task #11281) + + 2011-09-11: Simon Goldschmidt + * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize + initial local TCP/UDP ports (so that different port ranges are used after + a reboot; bug #33818; this one added tcp_init/udp_init functions again) + + 2011-09-03: Simon Goldschmidt + * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302) + + 2011-08-24: Simon Goldschmidt + * opt.h, netif.h/.c: added netif remove callback (bug #32397) + + 2011-07-26: Simon Goldschmidt + * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter + function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN) + + 2011-07-21: Simon Goldschmidt (patch by hanhui) + * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour: + Added pbuf flags to mark incoming packets as link-layer broadcast/multicast. + Also added code to allow ip_forward() to forward non-broadcast packets to + the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1). + + 2011-07-21: Simon Goldschmidt + * sockets.c, opt.h: (bug #30185): added LWIP_FIONREAD_LINUXMODE that makes + ioctl/FIONREAD return the size of the next pending datagram. + + 2011-06-26: Simon Goldschmidt (patch by Cameron Gutman) + * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that + pcb->state != LISTEN + + 2011-05-25: Simon Goldschmidt + * again nearly the whole stack, renamed ip.c to ip4.c, ip_addr.c to ip4_addr.c, + combined ipv4/ipv6 inet_chksum.c, added ip.h, ip_addr.h: Combined IPv4 + and IPv6 code where possible, added defines to access IPv4/IPv6 in non-IP + code so that the code is more readable. + + 2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt) + * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to + Ivan! (this is work in progress: we're just post release anyway :-) + + 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage) + * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static + memory message + + + ++ Bugfixes: + + 2013-01-15: Simon Goldschmidt + * ip4.c: fixed bug #37665 ip_canforward operates on address in wrong byte order + + 2013-01-15: Simon Goldschmidt + * pbuf.h: fixed bug #38097 pbuf_free_ooseq() warning + + 2013-01-14: Simon Goldschmidt + * dns.c: fixed bug #37705 Possible memory corruption in DNS query + + 2013-01-11: Simon Goldschmidt + * raw.c: fixed bug #38066 Raw pcbs can alter packet without eating it + + 2012-09-26: Simon Goldschmidt + * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7843 Fix corner case with dhcp timeouts + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet + + 2012-08-22: Simon Goldschmidt + * memp.c: fixed bug #37166: memp_sanity check loops itself + + 2012-08-13: Simon Goldschmidt + * dhcp.c: fixed bug #36645: Calling dhcp_release before dhcp_start + dereferences NULL + + 2012-08-13: Simon Goldschmidt + * msg_out.c: fixed bug #36840 snmp_send_trap() NULL de-reference if traps + configured but no interfaces available + + 2012-08-13: Simon Goldschmidt + * dns.c: fixed bug #36899 DNS TTL 0 is cached for a long time + + 2012-05-11: Simon Goldschmidt (patch by Marty) + * memp.c: fixed bug #36412: memp.c does not compile when + MEMP_OVERFLOW_CHECK > zero and MEMP_SEPARATE_POOLS == 1 + + 2012-05-08: Simon Goldschmidt + * tcp_out.c: fixed bug #36380: unsent_oversize mismatch in 1.4.1RC1 (this was + a debug-check issue only) + + 2012-05-03: Simon Goldschmidt (patch by Sylvain Rochet) + * ppp.c: fixed bug #36283 (PPP struct used on header size computation and + not packed) + + 2012-05-03: Simon Goldschmidt (patch by David Empson) + * ppp.c: fixed bug #36388 (PPP: checksum-only in last pbuf leads to pbuf with + zero length) + + 2012-03-27: Simon Goldschmidt + * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c + + 2012-03-27: Simon Goldschmidt (patch by Mason) + * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the + send MSS + + 2012-03-25: Simon Goldschmidt + * api_msg.c: Fixed bug #35817: do_connect() invalidly signals op_completed + for UDP/RAW with LWIP_TCPIP_CORE_LOCKING==1 + + 2012-03-25: Simon Goldschmidt + * api_msg.h, api_lib.c, api_msg.c, netifapi.c: fixed bug #35931: Name space + pollution in api_msg.c and netifapi.c + + 2012-03-22: Simon Goldschmidt + * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward + + 2012-03-20: Simon Goldschmidt (patch by Mason) + * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list + + 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic) + * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian, + possible bug on little endian system + + 2012-02-23: Simon Goldschmidt + * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt + * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP() + (bug #35541: PPP Memory Leak) + + 2012-02-16: Simon Goldschmidt + * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage) + * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed + + 2012-02-15: Simon Goldschmidt + * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with + MEMP_MEM_MALLOC==1 + + 2012-02-12: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on + MSS > pcb->snd_wnd (by not creating segments bigger than half the window) + + 2012-02-11: Simon Goldschmidt + * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait + queue while closing + + 2012-01-22: Simon Goldschmidt + * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR) + + 2012-01-21: Simon Goldschmidt + * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb + + 2012-01-20: Simon Goldschmidt + * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths + + 2012-01-20: Simon Goldschmidt + * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy + + 2011-11-25: Simon Goldschmidt + * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt + tcp_active_pcbs in some cases + + 2011-11-23: Simon Goldschmidt + * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with + '#ifndef sys_msleep' + + 2011-11-22: Simon Goldschmidt + * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when + netif is brought down + + 2011-10-28: Simon Goldschmidt + * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks + + 2011-10-23: Simon Goldschmidt + * mem.c: fixed bug #34429: possible memory corruption with + LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1 + + 2011-10-18: Simon Goldschmidt + * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard + error value + + 2011-10-18: Simon Goldschmidt + * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small + windows (bug #34176 select after non-blocking send times out) + + 2011-10-18: Simon Goldschmidt + * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't + consider netif->mtu, causes slow network + + 2011-10-18: Simon Goldschmidt + * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code + + 2011-10-18: Simon Goldschmidt + * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS + + 2011-10-17: Simon Goldschmidt + * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api + + 2011-10-13: Simon Goldschmidt + * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no + zero window is received) by starting the persist timer when a zero window is + received, not when we have more data queued for sending than fits into the + window + + 2011-10-13: Simon Goldschmidt + * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex + + 2011-10-13: Simon Goldschmidt + * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is + used and not all protocols are enabled + + 2011-10-12: Simon Goldschmidt + * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4 + + 2011-10-09: Simon Goldschmidt + * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect + byte value when pcb->unacked != NULL + + 2011-10-09: Simon Goldschmidt + * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong + + 2011-09-27: Simon Goldschmidt + * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places... + + 2011-09-27: Simon Goldschmidt + * tcp_in.c: fixed bug #28288: Data after FIN in oos queue + + 2011-09-27: Simon Goldschmidt + * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf + + 2011-09-24: Simon Goldschmidt + * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1 + + 2011-09-23: Simon Goldschmidt + * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for + the last packet including FIN can lose data + + 2011-09-22: Simon Goldschmidt + * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into + account + + 2011-09-21: Simon Goldschmidt + * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks + in init.c + + 2011-09-20: Simon Goldschmidt + * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts) + + 2011-09-11: Simon Goldschmidt + * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs + (bug #34019) + + 2011-09-09: Simon Goldschmidt + * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if + udp port matches + + 2011-09-03: Simon Goldschmidt + * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet + is aggregated and sent to application + + 2011-09-01: Simon Goldschmidt + * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared + to other options + + 2011-09-01: Simon Goldschmidt + * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno + + 2011-08-24: Simon Goldschmidt + * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard + + 2011-08-24: Simon Goldschmidt + * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling + accept() on UDP connections + + 2011-08-24: Simon Goldschmidt + * sockets.h: fixed bug #34057 socklen_t should be a typedef + + 2011-08-24: Simon Goldschmidt + * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo) + + 2011-08-24: Simon Goldschmidt + * dhcp.c: fixed bug #34122 dhcp: hostname can overflow + + 2011-08-24: Simon Goldschmidt + * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr + + 2011-08-22: Simon Goldschmidt + * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This + merely prevents nagle from not transmitting fast after closing.) + + 2011-07-22: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns + always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now + lwip_send() sends as much as possible for non-blocking sockets + + 2011-07-22: Simon Goldschmidt + * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented + for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level. + + 2011-07-21: Simon Goldschmidt + * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by + sending an ARP request when an ARP entry is used in the last minute before + it would time out. + + 2011-07-04: Simon Goldschmidt + * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0. + + 2011-06-26: Simon Goldschmidt + * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by + updating its documentation only. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an + unaligned pointer. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1" + + + +(STABLE-1.4.0) + + ++ New features: + + 2011-03-27: Simon Goldschmidt + * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and + calculate it in tcp_zero_window_probe (the only place where it was used). + + 2010-11-21: Simon Goldschmidt + * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif + (fixes bug #31525). + + 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage) + * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for + IP_MULTICAST_LOOP at socket- and raw-API level. + + 2010-06-16: Simon Goldschmidt + * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow + link-layer-addressed UDP traffic to be received while a netif is down (just + like DHCP during configuration) + + 2010-05-22: Simon Goldschmidt + * many many files: bug #27352: removed packing from ip_addr_t, the packed + version is now only used in protocol headers. Added global storage for + current src/dest IP address while in input functions. + + 2010-05-16: Simon Goldschmidt + * def.h: task #10391: Add preprocessor-macros for compile-time htonl + calculation (and use them throughout the stack where applicable) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool + instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own + MEMP pool instead of the heap + + 2010-05-13: Simon Goldschmidt + * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added + new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast + packets to more than one pcb. + + 2010-05-02: Simon Goldschmidt + * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending + UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-04-30: Simon Goldschmidt + * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that + take a precalculated checksum, added pbuf_fill_chksum() to copy data + into a pbuf and at the same time calculating the checksum for that data + + 2010-04-29: Simon Goldschmidt + * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying + 2-byte-aligned IP addresses and MAC addresses + + 2010-04-28: Patch by Bill Auerbach + * ip.c: Inline generating IP checksum to save a function call + + 2010-04-14: Simon Goldschmidt + * tcpip.h/.c, timers.c: Added an overridable define to get informed when the + tcpip_thread processes messages or timeouts to implement a watchdog. + + 2010-03-28: Simon Goldschmidt + * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing + fragment if LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-03-27: Simon Goldschmidt + * etharp.c: Speedup TX by moving code from find_entry to etharp_output/ + etharp_query to prevent unnecessary function calls (inspired by + patch #7135). + + 2010-03-20: Simon Goldschmidt + * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code + since the linker cannot do this automatically to save space. + + 2010-03-20: Simon Goldschmidt + * opt.h, etharp.c/.h: Added support for static ARP table entries + + 2010-03-14: Simon Goldschmidt + * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum + when creating TCP segments, not when (re-)transmitting them. + + 2010-03-07: Simon Goldschmidt + * sockets.c: bug #28775 (select/event_callback: only check select_cb_list + on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code. + This should speed up receiving data on sockets as the select code in + event_callback is only executed when select is waiting. + + 2010-03-06: Simon Goldschmidt + * tcp_out.c: task #7013 (Create option to have all packets delivered to + netif->output in one piece): Always copy to try to create single pbufs + in tcp_write. + + 2010-03-06: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv + by not allocating a netbuf): added function netconn_recv_tcp_pbuf() + for tcp netconns to receive pbufs, not netbufs; use that function + for tcp sockets. + + 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt + * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040: + Work on tcp_enqueue: Don't waste memory when chaining segments, + added option TCP_OVERSIZE to prevent creating many small pbufs when + calling tcp_write with many small blocks of data. Instead, pbufs are + allocated larger than needed and the space is used for later calls to + tcp_write. + + 2010-02-21: Simon Goldschmidt + * stats.c/.h: Added const char* name to mem- and memp-stats for easier + debugging. + + 2010-02-21: Simon Goldschmidt + * tcp.h (and usages), added tcp_impl.h: Splitted API and internal + implementation of tcp to make API usage cleare to application programmers + + 2010-02-14: Simon Goldschmidt/Stephane Lesage + * ip_addr.h: Improved some defines working on ip addresses, added faster + macro to copy addresses that cannot be NULL + + 2010-02-13: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non- + blocking send operation) + + 2010-02-12: Simon Goldschmidt + * sockets.c/.h: Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + 2010-02-12: Simon Goldschmidt + * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated + memory): added autoip_set_struct() and dhcp_set_struct() to let autoip + and dhcp work with user-allocated structs instead of callin mem_malloc + + 2010-02-12: Simon Goldschmidt/Jeff Barber + * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has + SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT + + 2010-02-12: Simon Goldschmidt + * sys layer: task #10139 (Prefer statically allocated memory): converted + mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t; + converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX + to let sys.h use binary semaphores instead of mutexes - as before) + + 2010-02-09: Simon Goldschmidt (Simon Kallweit) + * timers.c/.h: Added function sys_restart_timeouts() from patch #7085 + (Restart system timeout handling) + + 2010-02-09: Simon Goldschmidt + * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into + netif.c) - loopif does not have to be created by the port any more, + just define LWIP_HAVE_LOOPIF to 1. + + 2010-02-08: Simon Goldschmidt + * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa + inet_ntoa_r/ipaddr_ntoa_r + + 2010-02-08: Simon Goldschmidt + * netif.h: Added netif_s/get_igmp_mac_filter() macros + + 2010-02-05: Simon Goldschmidt + * netif.h: Added function-like macros to get/set the hostname on a netif + + 2010-02-04: Simon Goldschmidt + * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to + make changing the actual implementation behind the typedef easier. + + 2010-02-01: Simon Goldschmidt + * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool + for allocating memory when getaddrinfo() is called. + + 2010-01-31: Simon Goldschmidt + * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse + them once instead of parsing for every option. This also removes + the need for mem_malloc from dhcp_recv and makes it possible to + correctly retrieve the BOOTP file. + + 2010-01-30: simon Goldschmidt + * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect + the sockets array. + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, api_msg.c, sockets.c: Added except set support in select + (patch #6860) + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c: + Add non-blocking support for connect (partly from patch #6860), + plus many cleanups in socket & netconn API. + + 2010-01-27: Simon Goldschmidt + * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding + to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 + + 2010-01-26: Simon Goldschmidt + * snmp: Use memp pools for snmp instead of the heap; added 4 new pools. + + 2010-01-14: Simon Goldschmidt + * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback + by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback() + + 2010-01-13: Simon Goldschmidt + * mem.c: The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + (patch #6966 and bug #26133) + + 2010-01-10: Simon Goldschmidt (Bill Auerbach) + * opt.h, memp.c: patch #6822 (Add option to place memory pools in + separate arrays) + + 2010-01-10: Simon Goldschmidt + * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define + LWIP_RAND() for lwip-wide randomization (to be defined in cc.h) + + 2009-12-31: Simon Goldschmidt + * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h + added timers.c/.h: Separated timer implementation from semaphore/mbox + implementation, moved timer implementation to timers.c/.h, timers are + now only called from tcpip_thread or by explicitly checking them. + (TASK#7235) + + 2009-12-27: Simon Goldschmidt + * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option + LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE) + + + ++ Bugfixes: + + 2011-04-20: Simon Goldschmidt + * sys_arch.txt: sys_arch_timeouts() is not needed any more. + + 2011-04-13: Simon Goldschmidt + * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by + using ports in the IANA private/dynamic range (49152 through 65535). + + 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl: + * etharp.h/.c: Fixed broken VLAN support. + + 2011-03-27: Simon Goldschmidt + * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp + pcbs) by checking if the pcb was bound (local_port != 0). + + 2011-03-27: Simon Goldschmidt + * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice) + + 2011-03-27: Simon Goldschmidt + * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and + raw pcbs with LWIP_TCPIP_CORE_LOCKING==1. + + 2011-03-27: Simon Goldschmidt + * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route + is present never times out) by starting retransmission timer before checking + route. + + 2011-03-22: Simon Goldschmidt + * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only + calling sio_read_abort() if the file descriptor is valid. + + 2011-03-14: Simon Goldschmidt + * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect + more than once can render a socket useless) since it mainly involves changing + "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal. + + 2011-03-13: Simon Goldschmidt + * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing + err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN: + use EALRADY instead of -1 + + 2011-03-13: Simon Goldschmidt + * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the + connection has been aborted by err_tcp (since this is not a normal closing + procedure). + + 2011-03-13: Simon Goldschmidt + * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind + with pcb->state != CLOSED + + 2011-02-17: Simon Goldschmidt + * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in + documentation + + 2011-02-17: Simon Goldschmidt + * many files: Added missing U/UL modifiers to fix 16-bit-arch portability. + + 2011-01-24: Simon Goldschmidt + * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems + + 2010-12-02: Simon Goldschmidt + * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal. + + 2010-11-23: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for + LWIP_SO_RCVBUF and ioctl/FIONREAD. + + 2010-11-23: Simon Goldschmidt + * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at + least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet. + + 2010-11-23: Simon Goldschmidt + * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after + refusing 'refused_data' again. + + 2010-11-22: Simon Goldschmidt + * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS + after a successful nonblocking connection. + + 2010-11-22: Simon Goldschmidt + * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr + must be sent link-local + + 2010-11-22: Simon Goldschmidt + * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for + LWIP_TIMERS==0 + + 2010-11-20: Simon Goldschmidt + * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number + + 2010-11-20: Simon Goldschmidt + * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to + resemble other stacks. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else + no-copy TCP writes will never succeed. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does + not match documentation: return ERR_ARG instead of ERR_VAL if not + initialized or wrong argument. + + 2010-10-20: Simon Goldschmidt + * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16 + + 2010-10-05: Simon Goldschmidt + * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when + replugging the network cable after an AutoIP address was assigned. + + 2010-08-10: Simon Goldschmidt + * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs + + 2010-08-03: Simon Goldschmidt + * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625) + + 2010-08-01: Simon Goldschmidt (patch by Greg Renda) + * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big + endian architectures) + + 2010-07-28: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP + disabled. + + 2010-07-27: Simon Goldschmidt + * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no + harm but never did anything + + 2010-07-21: Simon Goldschmidt + * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not + add IP options) + + 2010-07-16: Kieran Mansley + * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator + + 2010-07-10: Simon Goldschmidt + * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options + + 2010-06-30: Simon Goldschmidt + * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in + netconn_delete) + + 2010-06-28: Kieran Mansley + * timers.c remove unportable printing of C function pointers + + 2010-06-24: Simon Goldschmidt + * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag + NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading + + 2010-06-24: Simon Goldschmidt + * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly + implemented shutdown at socket level. + + 2010-06-21: Simon Goldschmidt + * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has + problems with zero-copy DMA MACs) by adding custom pbufs and implementing + custom pbufs that reference other (original) pbufs. Additionally set + IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side. + + 2010-06-15: Simon Goldschmidt + * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses + + 2010-06-14: Simon Goldschmidt + * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses + + 2010-06-12: Simon Goldschmidt + * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop + state + + 2010-05-17: Simon Goldschmidt + * netdb.c: Correctly NULL-terminate h_addr_list + + 2010-05-16: Simon Goldschmidt + * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent + "symbol already defined" i.e. when linking to winsock + + 2010-05-05: Simon Goldschmidt + * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may + overflow) + + 2010-04-21: Simon Goldschmidt + * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening + connection) + + 2010-03-28: Luca Ceresoli + * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers + + 2010-03-27: Luca Ceresoli + * mib2.c: patch #7130: remove meaningless const qualifiers + + 2010-03-26: Simon Goldschmidt + * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too + + 2010-03-26: Simon Goldschmidt + * various files: Fixed compiling with different options disabled (TCP/UDP), + triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled + + 2010-03-25: Simon Goldschmidt + * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly + + 2010-03-25: Simon Goldschmidt + * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side + overrunning our rcv_wnd in ooseq case. + + 2010-03-22: Simon Goldschmidt + * tcp.c: tcp_listen() did not copy the pcb's prio. + + 2010-03-19: Simon Goldschmidt + * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set + + 2010-03-14: Simon Goldschmidt + * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports + where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h + and basing PBUF_LINK_HLEN on it. + + 2010-03-08: Simon Goldschmidt + * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections + when assiging routable address): when checking incoming packets and + aborting existing connection on address change, filter out link-local + addresses. + + 2010-03-06: Simon Goldschmidt + * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING + + 2010-03-06: Simon Goldschmidt + * ipv4/ip.c: Don't try to forward link-local addresses + + 2010-03-06: Simon Goldschmidt + * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal- + addresses to gw + + 2010-03-05: Simon Goldschmidt + * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type + and state. + + 2010-03-05: Simon Goldschmidt + * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split + into multiple calls to tcp_write. + + 2010-02-21: Simon Goldschmidt + * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep + the implementation of DNS_USES_STATIC_BUF==1) + + 2010-02-20: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement + close() vs. shutdown(). Now the application does not get any more + recv callbacks after calling tcp_close(). Added tcp_shutdown(). + + 2010-02-19: Simon Goldschmidt + * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent + confusion with realloc() + + 2010-02-15: Simon Goldschmidt/Stephane Lesage + * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK + (fixes bug #28899) + + 2010-02-14: Simon Goldschmidt + * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with + LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and + admin-status of a netif are up + + 2010-02-14: Simon Goldschmidt + * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet + reception and is not really necessary + + 2010-02-14: Simon Goldschmidt + * etharp.c/.h: Fixed ARP input processing: only add a new entry if a + request was directed as us (RFC 826, Packet Reception), otherwise + only update existing entries; internalized some functions + + 2010-02-14: Simon Goldschmidt + * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be + disabled on netif used for PPPoE) by adding a new netif flag + (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet + device but prevents usage of ARP (so that ethernet_input can be used + for PPPoE). + + 2010-02-12: Simon Goldschmidt + * netif.c: netif_set_link_up/down: only do something if the link state + actually changes + + 2010-02-12: Simon Goldschmidt/Stephane Lesage + * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking + connect) + + 2010-02-12: Simon Goldschmidt + * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h) + + 2010-02-09: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110 + (recv() makes receive window update for data that wasn't received by + application) + + 2010-02-09: Simon Goldschmidt/Stephane Lesage + * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out + or any netconn_recv() error) + + 2010-02-09: Simon Goldschmidt + * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets) + + 2010-02-09: Simon Goldschmidt + * netif.c: For loopback packets, adjust the stats- and snmp-counters + for the loopback netif. + + 2010-02-08: Simon Goldschmidt + * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity + since they are not used anywhere else. + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats + (patch from bug #28798) + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and + another bug when LWIP_RAND() returns zero. + + 2010-02-04: Simon Goldschmidt + * nearly every file: Use macros defined in ip_addr.h (some of them new) + to work with IP addresses (preparation for bug #27352 - Change ip_addr + from struct to typedef (u32_t) - and better code). + + 2010-01-31: Simon Goldschmidt + * netif.c: Don't call the link-callback from netif_set_up/down() since + this invalidly retriggers DHCP. + + 2010-01-29: Simon Goldschmidt + * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the + portability file inet.h and its contents from the stack: moved htonX- + functions to def.h (and the new def.c - they are not ipv4 dependent), + let inet.h depend on ip_addr.h and not the other way round. + This fixes bug #28732. + + 2010-01-28: Kieran Mansley + * tcp.c: Ensure ssthresh >= 2*MSS + + 2010-01-27: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv + callback can lead to accessing unallocated memory. As a consequence, + ERR_ABRT means the application has called tcp_abort()! + + 2010-01-25: Simon Goldschmidt + * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY + not implemented in SNMP): write-only or not-accessible are still + returned by getnext (though not by get) + + 2010-01-24: Simon Goldschmidt + * snmp: Renamed the private mib node from 'private' to 'mib_private' to + not use reserved C/C++ keywords + + 2010-01-23: Simon Goldschmidt + * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less + than 1 ms + + 2010-01-21: Simon Goldschmidt + * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called + if tcp_enqueue fails) both in raw- and netconn-API + + 2010-01-19: Simon Goldschmidt + * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp + + 2010-01-18: Iordan Neshev/Simon Goldschmidt + * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some + bugfix backports from 2.4.x. + + 2010-01-18: Simon Goldschmidt + * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong + + 2010-01-17: Simon Goldschmidt + * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c): + task #10102: "netconn: clean up conn->err threading issues" by adding + error return value to struct api_msg_msg + + 2010-01-17: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept() + to return err_t (bugs #27709 and #28087) + + 2010-01-14: Simon Goldschmidt + * ...: Use typedef for function prototypes throughout the stack. + + 2010-01-13: Simon Goldschmidt + * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive + window = 0) by correctly draining recvmbox/acceptmbox + + 2010-01-11: Simon Goldschmidt + * pap.c: Fixed bug #13315 (PPP PAP authentication can result in + erroneous callbacks) by copying the code from recent pppd + + 2010-01-10: Simon Goldschmidt + * raw.c: Fixed bug #28506 (raw_bind should filter received packets) + + 2010-01-10: Simon Goldschmidt + * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)()) + + 2010-01-08: Simon Goldschmidt + * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535) + + 2010-01-08: Simon Goldschmidt + * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string + passed to dns_local_addhost() might be volatile + + 2010-01-07: Simon Goldschmidt + * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too + + 2010-01-06: Simon Goldschmidt + * netdb.h: Fixed bug #28496: missing include guards in netdb.h + + 2009-12-31: Simon Goldschmidt + * many ppp files: Reorganised PPP source code from ucip structure to pppd + structure to easily compare our code against the pppd code (around v2.3.1) + + 2009-12-27: Simon Goldschmidt + * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted + unit test + + +(STABLE-1.3.2) + + ++ New features: + + 2009-10-27 Simon Goldschmidt/Stephan Lesage + * netifapi.c/.h: Added netifapi_netif_set_addr() + + 2009-10-07 Simon Goldschmidt/Fabian Koch + * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to + support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO) + + 2009-08-26 Simon Goldschmidt/Simon Kallweit + * slipif.c/.h: bug #26397: SLIP polling support + + 2009-08-25 Simon Goldschmidt + * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN), + New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK. + + 2009-08-25 Simon Goldschmidt + * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*) + + 2009-08-24 Jakob Stoklund Olesen + * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond + to netif_set_link_up(). + + 2009-08-23 Simon Goldschmidt + * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state + to a human-readable string. + + ++ Bugfixes: + + 2009-12-24: Kieran Mansley + * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing + (BUG#28241) + + 2009-12-06: Simon Goldschmidt + * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can + be statically allocated (like in ucip) + + 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev) + * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT + + 2009-12-03: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit + could have non-zero length + + 2009-12-02: Simon Goldschmidt + * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting + tcp_input_pcb until after calling the pcb's callbacks + + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + + 2009-11-29: Simon Goldschmidt + * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by + queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty + + 2009-11-26: Simon Goldschmidt + * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending + segment + + 2009-11-26: Simon Goldschmidt + * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle + algorithm at PCB level + + 2009-11-22: Simon Goldschmidt + * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent + + 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach) + * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when + reusing time-wait pcb + + 2009-11-20: Simon Goldschmidt (patch by Albert Bartel) + * sockets.c: Fixed bug #28062: Data received directly after accepting + does not wake up select + + 2009-11-11: Simon Goldschmidt + * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo) + + 2009-10-30: Simon Goldschmidt + * opt.h: Increased default value for TCP_MSS to 536, updated default + value for TCP_WND to 4*TCP_MSS to keep delayed ACK working. + + 2009-10-28: Kieran Mansley + * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code + to follow algorithm from TCP/IP Illustrated + + 2009-10-27: Kieran Mansley + * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK + + 2009-10-25: Simon Goldschmidt + * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if + pcb->recv is NULL to keep rcv_wnd correct) + + 2009-10-25: Simon Goldschmidt + * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state + + 2009-10-23: Simon Goldschmidt (David Empson) + * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes + + 2009-10-21: Simon Goldschmidt + * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and + trailing 1 byte len (SYN/FIN) + + 2009-10-21: Simon Goldschmidt + * tcp_out.c: Fixed bug #27315: zero window probe and FIN + + 2009-10-19: Simon Goldschmidt + * dhcp.c/.h: Minor code simplification (don't store received pbuf, change + conditional code to assert where applicable), check pbuf length before + testing for valid reply + + 2009-10-19: Simon Goldschmidt + * dhcp.c: Removed most calls to udp_connect since they aren't necessary + when using udp_sendto_if() - always stay connected to IP_ADDR_ANY. + + 2009-10-16: Simon Goldschmidt + * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop + valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is + enabled + + 2009-10-15: Simon Goldschmidt (Oleg Tyshev) + * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit + + 2009-10-15: Simon Goldschmidt + * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv() + timeout + + 2009-10-15: Simon Goldschmidt + * autoip.c: Fixed bug #27704: autoip starts with wrong address + LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead + of network byte order + + 2009-10-11 Simon Goldschmidt (Jörg Kesten) + * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments + which are not consecutive when retransmitting unacked segments + + 2009-10-09 Simon Goldschmidt + * opt.h: Fixed default values of some stats to only be enabled if used + Fixes bug #27338: sys_stats is defined when NO_SYS = 1 + + 2009-08-30 Simon Goldschmidt + * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK + function" by checking for loopback before calling ip_frag + + 2009-08-25 Simon Goldschmidt + * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0 + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27078: Possible memory leak in pppInit() + + 2009-08-23 Simon Goldschmidt + * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result + is error. + + 2009-08-23 Simon Goldschmidt + * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF + Fixed wrong parenthesis, added check in init.c + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms + + 2009-08-23 Simon Goldschmidt + * many ppp files: bug #27267: Added include to string.h where needed + + 2009-08-23 Simon Goldschmidt + * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian) + + +(STABLE-1.3.1) + + ++ New features: + + 2009-05-10 Simon Goldschmidt + * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option + LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only + one pbuf to help MACs that don't support scatter-gather DMA. + + 2009-05-09 Simon Goldschmidt + * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming + ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + + 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen + * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive + extended info about the currently received packet. + + 2009-04-27 Simon Goldschmidt + * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1 + + 2009-04-25 Simon Goldschmidt + * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next + bigger malloc pool if one is empty (only usable with MEM_USE_POOLS). + + 2009-04-21 Simon Goldschmidt + * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static + hosts table. New configuration options DNS_LOCAL_HOSTLIST and + DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined + as an external function for lookup. + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for + TCP timestamp options, off by default. Rework tcp_enqueue() to + take option flags rather than specified option data + + 2009-02-18 Simon Goldschmidt + * cc.h: Added printf formatter for size_t: SZT_F + + 2009-02-16 Simon Goldschmidt (patch by Rishi Khan) + * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast + pings + + 2009-02-12 Simon Goldschmidt + * init.h: Added LWIP_VERSION to get the current version of the stack + + 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler) + * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead + of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc + is otherwise used) + + 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach) + * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial() + is only used by UDPLITE at present, so conditionalise it. + + 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli) + * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP + "seed" address. This should reduce AUTOIP conflicts if + LWIP_AUTOIP_CREATE_SEED_ADDR is overridden. + + 2008-10-02 Jonathan Larmour and Rishi Khan + * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking + socket. + + 2008-06-30 Simon Goldschmidt + * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from + interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows + mem_free to run between mem_malloc iterations. Added illegal counter for + mem stats. + + 2008-06-27 Simon Goldschmidt + * stats.h/.c, some other files: patch #6483: stats module improvement: + Added defines to display each module's statistic individually, added stats + defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. + + 2008-06-17 Simon Goldschmidt + * err.h: patch #6459: Made err_t overridable to use a more efficient type + (define LWIP_ERR_T in cc.h) + + 2008-06-17 Simon Goldschmidt + * slipif.c: patch #6480: Added a configuration option for slipif for symmetry + to loopif + + 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) + * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly + modified version of patch # 6370: Moved loopif code to netif.c so that + loopback traffic is supported on all netifs (all local IPs). + Added option to limit loopback packets for each netifs. + + + ++ Bugfixes: + 2009-08-12 Kieran Mansley + * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when + out of window or out of order properly + + 2009-08-12 Kieran Mansley + * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1 + + 2009-07-28 Simon Goldschmidt + * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s + + 2009-07-27 Kieran Mansley + * api.h api_msg.h netdb.h sockets.h: add missing #include directives + + 2009-07-09 Kieran Mansley + * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for + recv_avail and don't increment counters until message successfully + sent to mbox + + 2009-06-25 Kieran Mansley + * api_msg.c api.h: BUG26722: initialise netconn write variables + in netconn_alloc + + 2009-06-25 Kieran Mansley + * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set + + 2009-06-25 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct + simultaneous close behaviour, and make snd_nxt have the same meaning + as in the RFCs. + + 2009-05-12 Simon Goldschmidt + * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on + arp_table / uses etharp_query" by adding etharp_gratuitous() + + 2009-05-12 Simon Goldschmidt + * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options + to the IP header (used by igmp_ip_output_if) + + 2009-05-06 Simon Goldschmidt + * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if + defined) for SWAP_BYTES_IN_WORD to speed up checksumming. + + 2009-05-05 Simon Goldschmidt + * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select() + to crash + + 2009-05-04 Simon Goldschmidt + * init.c: snmp was not initialized in lwip_init() + + 2009-05-04 Frédéric Bernon + * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled. + + 2009-05-03 Simon Goldschmidt + * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full + (and unsent->next == NULL) + + 2009-05-02 Simon Goldschmidt + * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after + 1.3.0 in CVS only) - fixes compilation of ppp_oe.c + + 2009-05-02 Simon Goldschmidt + * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields + + 2009-05-01 Simon Goldschmidt + * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets + + 2009-05-01 Simon Goldschmidt + * ppp.c: bug #24228: Memory corruption with PPP and DHCP + + 2009-04-29 Frédéric Bernon + * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the + SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception + of broadcast packets even when this option wasn't set. Port maintainers + which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h. + If you want this option also filter broadcast on recv operations, you also + have to set IP_SOF_BROADCAST_RECV=1 in opt.h. + + 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen + * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and + DHCP/AUTOIP cooperation + + 2009-04-25 Simon Goldschmidt, Oleg Tyshev + * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd + Fixed by sorting the unsent and unacked queues (segments are inserted at the + right place in tcp_output and tcp_rexmit). + + 2009-04-25 Simon Goldschmidt + * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation + when debugging": memp_sizes contained the wrong sizes (including sanity + regions); memp pools for MEM_USE_POOLS were too small + + 2009-04-24 Simon Goldschmidt, Frédéric Bernon + * inet.c: patch #6765: Fix a small problem with the last changes (incorrect + behavior, with with ip address string not ended by a '\0', a space or a + end of line) + + 2009-04-19 Simon Goldschmidt + * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails, + pcb->err is called, not pcb->connected (with an error code). + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with + no-copy-tcpwrite": deallocate option data, only concat segments with same flags + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated + in the header pbuf, not the data pbuf) + + 2009-04-18 Simon Goldschmidt + * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore() + + 2009-04-15 Simon Goldschmidt + * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp + + 2009-04-15 Simon Goldschmidt + * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in + + 2009-04-15 Simon Goldschmidt + * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function + ip_hinted_output() (for smaller code mainly) + + 2009-04-15 Simon Goldschmidt + * inet.c: patch #6765: Supporting new line characters in inet_aton() + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option; + Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu + is big enough in dhcp_start + + 2009-04-15 Simon Goldschmidt + * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak + + 2009-04-15 Simon Goldschmidt + * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY + + 2009-04-15 Simon Goldschmidt + * sockets.c: bug #26121: set_errno can be overridden + + 2009-04-09 Kieran Mansley (patch from Luca Ceresoli ) + * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when + LWIP_TCP==0 + + 2009-04-09 Kieran Mansley (patch from Roy Lee ) + * tcp.h: Patch#6802 Add do-while-clauses to those function like + macros in tcp.h + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window + updates are calculated and sent (BUG20515) + + * tcp_in.c: cope with SYN packets received during established states, + and retransmission of initial SYN. + + * tcp_out.c: set push bit correctly when tcp segments are merged + + 2009-03-27 Kieran Mansley + * tcp_out.c set window correctly on probes (correcting change made + yesterday) + + 2009-03-26 Kieran Mansley + * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping + connections where no reset required (bug #25622) + + * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes + (bug #20779) + + 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach) + * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be + too small depending on MEM_ALIGNMENT + + 2009-02-16 Simon Goldschmidt + * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard; + converted size argument of netconn_write to 'size_t' + + 2009-02-16 Simon Goldschmidt + * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host + by moving accept callback function pointer to TCP_PCB_COMMON + + 2009-02-12 Simon Goldschmidt + * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size" + option) + + 2009-02-11 Simon Goldschmidt + * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start) + + 2009-02-11 Simon Goldschmidt + * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize: + RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv()) + + 2009-02-10 Simon Goldschmidt + * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD: + Accepts_pending is decrease on a corresponding listen pcb when a connection + in state SYN_RCVD is close. + + 2009-01-28 Jonathan Larmour + * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run + out of pool pbufs. + + 2008-12-19 Simon Goldschmidt + * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 + + 2008-12-10 Tamas Somogyi, Frédéric Bernon + * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and + port uses deleted netbuf. + + 2008-10-18 Simon Goldschmidt + * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length + in tcp_parseopt + + 2008-10-15 Simon Goldschmidt + * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers + by packing the struct ip_reass_helper. + + 2008-10-03 David Woodhouse, Jonathan Larmour + * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address. + + 2008-10-02 Jonathan Larmour + * dns.c: Hard-code structure sizes, to avoid issues on some compilers where + padding is included. + + 2008-09-30 Jonathan Larmour + * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an + assertion check that addrlen isn't NULL. + + 2008-09-30 Jonathan Larmour + * tcp.c: Fix bug #24227, wrong error message in tcp_bind. + + 2008-08-26 Simon Goldschmidt + * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and + inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h + + 2008-08-14 Simon Goldschmidt + * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when + tcp_close returns != ERR_OK) + + 2008-07-08 Frédéric Bernon + * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters + in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). + + 2008-06-24 Jonathan Larmour + * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused + if tcp_seg_copy fails. + + 2008-06-17 Simon Goldschmidt + * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) + and created defines for swapping bytes and folding u32 to u16. + + 2008-05-30 Kieran Mansley + * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd + rather than rcv_ann_wnd when deciding if packets are in-window. + Contributed by + + 2008-05-30 Kieran Mansley + * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow + passing as function pointers when MEM_LIBC_MALLOC is defined. + + 2008-05-09 Jonathan Larmour + * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to + stop it being treated as a fatal error. + + 2008-04-15 Simon Goldschmidt + * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP + (flag now cleared) + + 2008-03-27 Simon Goldschmidt + * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free + from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 + in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs + or heap memory from interrupt context + + 2008-03-26 Simon Goldschmidt + * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote + host sent a zero mss as TCP option. + + +(STABLE-1.3.0) + + ++ New features: + + 2008-03-10 Jonathan Larmour + * inet_chksum.c: Allow choice of one of the sample algorithms to be + made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM. + + 2008-01-22 Frédéric Bernon + * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in + TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names. + + 2008-01-14 Frédéric Bernon + * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable + to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the + tcp_recv callback (see rawapi.txt). + + 2008-01-14 Frédéric Bernon, Marc Chaland + * ip.c: Integrate patch #6369" ip_input : checking before realloc". + + 2008-01-12 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::sem per netconn::op_completed like suggested for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-12 Frédéric Bernon + * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE, + DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues + sizes), like suggested for the task #7490 "Add return value to sys_mbox_post". + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490 + "Add return value to sys_mbox_post". tcpip_callback is always defined as + "blocking" ("block" parameter = 1). + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-05 Frédéric Bernon + * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: + Introduce changes for task #7490 "Add return value to sys_mbox_post" with some + modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which + indicate the number of pointers query by the mailbox. There is three defines + in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the + netconn::acceptmbox. Port maintainers, you can decide to just add this new + parameter in your implementation, but to ignore it to keep the previous behavior. + The new sys_mbox_trypost function return a value to know if the mailbox is + full or if the message is posted. Take a look to sys_arch.txt for more details. + This new function is used in tcpip_input (so, can be called in an interrupt + context since the function is not blocking), and in recv_udp and recv_raw. + + 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c, + tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the + "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add + documentation in the rawapi.txt file. + + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer + + 2007-12-31 Frédéric Bernon, Luca Ceresoli + * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets + in autoip". The change in etharp_raw could be removed, since all calls to + etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be + wrong in the future. + + 2007-12-30 Frédéric Bernon, Tom Evans + * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address + Filtering" reported by Tom Evans. + + 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c, + sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API + applications have to call 'tcp_accepted(pcb)' in their accept callback to + keep accepting new connections. + + 2007-12-13 Frédéric Bernon + * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result" + by err_t type. Add a new err_t code "ERR_INPROGRESS". + + 2007-12-12 Frédéric Bernon + * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles + are the one which have ram usage. + + 2007-12-05 Frédéric Bernon + * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static + set of variables (=0) or a local one (=1). In this last case, your port should + provide a function "struct hostent* sys_thread_hostent( struct hostent* h)" + which have to do a copy of "h" and return a pointer ont the "per-thread" copy. + + 2007-12-03 Simon Goldschmidt + * ip.c: ip_input: check if a packet is for inp first before checking all other + netifs on netif_list (speeds up packet receiving in most cases) + + 2007-11-30 Simon Goldschmidt + * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access + UDP: move a (connected) pcb selected for input to the front of the list of + pcbs so that it is found faster next time. Same for RAW pcbs that have eaten + a packet. + + 2007-11-28 Simon Goldschmidt + * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS + + 2007-11-25 Simon Goldschmidt + * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy + algorithm. + + 2007-11-24 Simon Goldschmidt + * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c + to the new file netdb.c; included lwip_getaddrinfo. + + 2007-11-21 Simon Goldschmidt + * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss + based on the MTU of the netif used to send. Enabled by default. Disable by + setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492. + + 2007-11-19 Frédéric Bernon + * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name + received match the name query), implement DNS_USES_STATIC_BUF (the place where + copy dns payload to parse the response), return an error if there is no place + for a new query, and fix some minor problems. + + 2007-11-16 Simon Goldschmidt + * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c + removed files: core/inet.c, core/inet6.c + Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into + inet and chksum part; changed includes in all lwIP files as appropriate + + 2007-11-16 Simon Goldschmidt + * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential + dns resolver function for netconn api (netconn_gethostbyname) and socket api + (gethostbyname/gethostbyname_r). + + 2007-11-15 Jim Pettinato, Frédéric Bernon + * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name + requests with RAW api interface. Initialization is done in lwip_init() with + build time options. DNS timer is added in tcpip_thread context. DHCP can set + DNS server ip addresses when options are received. You need to set LWIP_DNS=1 + in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get + some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo" + list with points to improve. + + 2007-11-06 Simon Goldschmidt + * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly + enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status + for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined. + + 2007-11-06 Simon Goldschmidt + * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include + core header files in api.h (ip/tcp/udp/raw.h) to hide the internal + implementation from netconn api applications. + + 2007-11-03 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP & + RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled + by default). Netconn API users can use the netconn_recv_bufsize macro to access + it. This is a first release which have to be improve for TCP. Note it used the + netconn::recv_avail which need to be more "thread-safe" (note there is already + the problem for FIONREAD with lwip_ioctl/ioctlsocket). + + 2007-11-01 Frédéric Bernon, Marc Chaland + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c: + Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api + layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api + layer. This option enable to delayed TCP PUSH flag on multiple "write" calls. + Note that previous "copy" parameter for "write" APIs is now called "apiflags". + + 2007-10-24 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than + TCP_EVENT_xxx macros to get a code more readable. It could also help to remove + some code (like we have talk in "patch #5919 : Create compile switch to remove + select code"), but it could be done later. + + 2007-10-08 Simon Goldschmidt + * many files: Changed initialization: many init functions are not needed any + more since we now rely on the compiler initializing global and static + variables to zero! + + 2007-10-06 Simon Goldschmidt + * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY + to enqueue the received pbufs so that multiple packets can be reassembled + simultaneously and no static reassembly buffer is needed. + + 2007-10-05 Simon Goldschmidt + * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so + all netifs (or ports) can use it. + + 2007-10-05 Frédéric Bernon + * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the + common function to reduce a little bit the footprint (for all functions using + only the "netif" parameter). + + 2007-10-03 Frédéric Bernon + * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down, + netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce + a little bit the footprint (for all functions using only the "netif" parameter). + + 2007-09-15 Frédéric Bernon + * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF + option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for + netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for + IP_MULTICAST_TTL and IP_MULTICAST_IF. + + 2007-09-10 Frédéric Bernon + * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles + even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime() + each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can + decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but + call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime() + or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. + This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside + snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only + when it's queried (any direct call to "sysuptime" is changed by a call to + snmp_get_sysuptime). + + 2007-09-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP, + and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags + if you want IGMP on an interface. igmp_stop() is now called inside netif_remove(). + igmp_report_groups() is now called inside netif_set_link_up() (need to have + LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait + the next query message to receive the matching multicast streams). + + 2007-09-08 Frédéric Bernon + * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains + IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change). + Use this new field to access to common pcb fields (ttl, tos, so_options, etc...). + Enable to access to these fields with LWIP_TCP=0. + + 2007-09-05 Frédéric Bernon + * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h, + ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option + LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default). + Be careful, disabling ICMP make your product non-compliant to RFC1122, but + help to reduce footprint, and to reduce "visibility" on the Internet. + + 2007-09-05 Frédéric Bernon, Bill Florac + * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list + for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new + parameters have to be provided: a task name, and a task stack size. For this + one, since it's platform dependant, you could define the best one for you in + your lwipopts.h. For port maintainers, you can just add these new parameters + in your sys_arch.c file, and but it's not mandatory, use them in your OS + specific functions. + + 2007-09-05 Frédéric Bernon + * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings + inside init.c for task #7142 "Sanity check user-configurable values". + + 2007-09-04 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by + memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the + value). It will avoid potential fragmentation problems, use a counter to know + how many times a group is used on an netif, and free it when all applications + leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity + check if LWIP_IGMP!=0). + + 2007-09-03 Frédéric Bernon + * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement". + Initialize igmp_mac_filter to NULL in netif_add (this field should be set in + the netif's "init" function). Use the "imr_interface" field (for socket layer) + and/or the "interface" field (for netconn layer), for join/leave operations. + The igmp_join/leavegroup first parameter change from a netif to an ipaddr. + This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany). + + 2007-08-30 Frédéric Bernon + * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions + from api/api_lib". Now netbuf API is independant of netconn, and can be used + with other API (application based on raw API, or future "socket2" API). Ports + maintainers just have to add src/api/netbuf.c in their makefile/projects. + + 2007-08-30 Frédéric Bernon, Jonathan Larmour + * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check + user-configurable values". + + 2007-08-29 Frédéric Bernon + * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start. + igmp_start is call inside netif_add. Now, igmp initialization is in the same + spirit than the others modules. Modify some IGMP debug traces. + + 2007-08-29 Frédéric Bernon + * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function" + Add lwip_init function to regroup all modules initializations, and to provide + a place to add code for task #7142 "Sanity check user-configurable values". + Ports maintainers should remove direct initializations calls from their code, + and add init.c in their makefiles. Note that lwip_init() function is called + inside tcpip_init, but can also be used by raw api users since all calls are + disabled when matching options are disabled. Also note that their is new options + in opt.h, you should configure in your lwipopts.h (they are enabled per default). + + 2007-08-26 Marc Boucher + * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL + since they can under certain circumstances be called with an invalid conn + pointer after the connection has been closed (and conn has been freed). + + 2007-08-25 Frédéric Bernon (Artem Migaev's Patch) + * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up". + Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set. + + 2007-08-22 Frédéric Bernon + * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK + to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release. + + 2007-08-22 Frédéric Bernon + * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT & + ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the + name is tcpip_input (we keep the name of 1.2.0 function). + + 2007-08-17 Jared Grubb + * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool + settings into new memp_std.h and optional user file lwippools.h. This adds + more dynamic mempools, and allows the user to create an arbitrary number of + mempools for mem_malloc. + + 2007-08-16 Marc Boucher + * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function; + otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely + close the connection. + + 2007-08-16 Marc Boucher + * sockets.c: lwip_accept(): check netconn_peer() error return. + + 2007-08-16 Marc Boucher + * mem.c, mem.h: Added mem_calloc(). + + 2007-08-16 Marc Boucher + * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT) + for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG + and starving other message types. + Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API + + 2007-08-16 Marc Boucher + * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf + type and flgs (later renamed to flags). + Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*. + Improved lwip_recvfrom(). TCP push now propagated. + + 2007-08-16 Marc Boucher + * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global + provided by etharp. + + 2007-08-16 Marc Boucher + * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h, + etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c: + Added PPPoE support and various PPP improvements. + + 2007-07-25 Simon Goldschmidt + * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial, + making netbuf_copy_partial use this function. + + 2007-07-25 Simon Goldschmidt + * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with + 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and + other stacks. + + 2007-07-13 Jared Grubb (integrated by Frédéric Bernon) + * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add + a link callback in the netif struct, and functions to handle it. Be carefull + for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c) + if you want to be sure to be compatible with future changes... + + 2007-06-30 Frédéric Bernon + * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions. + + 2007-06-21 Simon Goldschmidt + * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both + LWIP_AUTOIP =0 and =1 to remove redundant code. + + 2007-06-21 Simon Goldschmidt + * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option + MEM_USE_POOLS to use 4 pools with different sized elements instead of a + heap. This both prevents memory fragmentation and gives a higher speed + at the cost of more memory consumption. Turned off by default. + + 2007-06-21 Simon Goldschmidt + * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of + netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into + int to be able to send a bigger buffer than 64K with one time (mainly + used from lwip_send). + + 2007-06-21 Simon Goldschmidt + * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write + into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too. + + 2007-06-21 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in + netconn_write from api_lib.c to api_msg.c to also prevent multiple context- + changes on low memory or empty send-buffer. + + 2007-06-18 Simon Goldschmidt + * etharp.c, etharp.h: Changed etharp to use a defined hardware address length + of 6 to avoid loading netif->hwaddr_len every time (since this file is only + used for ethernet and struct eth_addr already had a defined length of 6). + + 2007-06-17 Simon Goldschmidt + * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets + to disable UDP checksum generation on transmit. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid + pointers or parameters, and let the possibility to redefined it in cc.h. Use + this macro to check "conn" parameter in api_msg.c functions. + + 2007-06-11 Simon Goldschmidt + * sockets.c, sockets.h: Added UDP lite support for sockets + + 2007-06-10 Simon Goldschmidt + * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled + by default) to switch off UDP-Lite support if not needed (reduces udp.c code + size) + + 2007-06-09 Dominik Spies (integrated by Frédéric Bernon) + * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h: + AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and + LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt + (see TODO mark in the source code). + + 2007-06-09 Simon Goldschmidt + * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for + etharp_output() to match netif->output so etharp_output() can be used + directly as netif->output to save one function call. + + 2007-06-08 Simon Goldschmidt + * netif.h, ethernetif.c, slipif.c, loopif.c: Added define + NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables, + added initialization of those to ethernetif, slipif and loopif. + + 2007-05-18 Simon Goldschmidt + * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF + (defaulting to off for now) that can be set to 0 to send fragmented + packets by passing PBUF_REFs down the stack. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP + connections, such present in patch #5959. + + 2007-05-23 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx + code in only one part... + + 2007-05-18 Simon Goldschmidt + * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp + elements to overflow. This is achieved by adding some bytes before and after + each pool element (increasing their size, of course), filling them with a + prominent value and checking them on freeing the element. + Set it to 2 to also check every element in every pool each time memp_malloc() + or memp_free() is called (slower but more helpful). + + 2007-05-10 Simon Goldschmidt + * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for + PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce + code size. + + 2007-05-11 Frédéric Bernon + * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c: + Include a function pointer instead of a table index in the message to reduce + footprint. Disable some part of lwip_send and lwip_sendto if some options are + not set (LWIP_TCP, LWIP_UDP, LWIP_RAW). + + 2007-05-10 Simon Goldschmidt + * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus + \ extern "C" {' in all header files. Now you can write your application using + the lwIP stack in C++ and simply #include the core files. Note I have left + out the netif/ppp/*h header files for now, since I don't know which files are + included by applications and which are for internal use only. + + 2007-05-09 Simon Goldschmidt + * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library + memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for + situations where some compilers might inline the copy and save a function + call. Also replaced all calls to memcpy() with calls to (S)MEMCPY(). + + 2007-05-08 Simon Goldschmidt + * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc()) + to be overriden in case the C-library malloc implementation is not protected + against concurrent access. + + 2007-05-04 Simon Goldschmidt (Atte Kojo) + * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending + multiple packets to the same host. + + 2007-05-04 Frédéric Bernon, Jonathan Larmour + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible + to corrupt remote addr/port connection state". Reduce problems "not enought memory" with + netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between + sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function. + Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct, + these fields are now renamed "addr" & "port". + + 2007-04-11 Jonathan Larmour + * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new + sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return + with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro + by the port in sys_arch.h if desired. + + 2007-04-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API + allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp + clients, using new functions from netifapi.h. Disable as default (no port change to do). + + 2007-04-05 Frédéric Bernon + * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant. + + 2007-04-04 Simon Goldschmidt + * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x) + use this for and architecture-independent form to tell the compiler you intentionally + are not using this variable. Can be overriden in cc.h. + + 2007-03-28 Frédéric Bernon + * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to + define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded + string, point on one of your's ethernetif field, or alloc a string you will free yourself). + It will be used by DHCP to register a client hostname, but can also be use when you call + snmp_set_sysname. + + 2007-03-28 Frédéric Bernon + * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to + initialize a network interface's flag with. It tell this interface is an ethernet + device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility + Support for IPv4" section 4.6) when interface is "up" with netif_set_up(). + + 2007-03-26 Frédéric Bernon, Jonathan Larmour + * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build + time if you only use PPP or SLIP. The default is enable. Note we don't have to call + etharp_init in your port's initilization sequence if you use tcpip.c, because this call + is done in tcpip_init function. + + 2007-03-22 Frédéric Bernon + * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the + new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in + your lwipopts.h. More, unused counters are not defined in the stats structs, and not + display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined + but never used. Fix msg_in.c with the correct #if test for a stat display. + + 2007-03-21 Kieran Mansley + * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). + Provides callback on netif up/down state change. + + 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c, + ip.c, netif.h, tcpip.c, opt.h: + New configuration option LWIP_IGMP to enable IGMP processing. Based on only one + filter per all network interfaces. Declare a new function in netif to enable to + control the MAC filter (to reduce lwIP traffic processing). + + 2007-03-11 Frédéric Bernon + * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can + be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this + unless you know what you're doing (default are RFC1122 compliant). Note + that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds. + + 2007-03-08 Frédéric Bernon + * tcp.h: Keepalive values can be configured at compile time, but don't change + this unless you know what you're doing (default are RFC1122 compliant). + + 2007-03-08 Frédéric Bernon + * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h: + Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO + on UDP sockets/netconn. + + 2007-03-08 Simon Goldschmidt + * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time. + + 2007-03-06 Frédéric Bernon + * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: + Implement SO_RCVTIMEO on UDP sockets/netconn. + + 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt) + * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated + on the stack and remove the API msg type from memp + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * sockets.h, sockets.c: Move socket initialization to new + lwip_socket_init() function. + NOTE: this changes the API with ports. Ports will have to be + updated to call lwip_socket_init() now. + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * api_lib.c: Use memcpy in netbuf_copy_partial. + + + ++ Bug fixes: + + 2008-03-17 Frédéric Bernon, Ed Kerekes + * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have + some problems to fill the IP header on some targets, use now the + ip.h macros to do it). + + 2008-03-13 Frédéric Bernon + * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using + (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a + TCP connection caused a crash. Note that using (lwip_)recvfrom + like this is a bit slow and that using (lwip)getpeername is the + good lwip way to do it (so, using recv is faster on tcp sockets). + + 2008-03-12 Frédéric Bernon, Jonathan Larmour + * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's + recv_raw() does not consume data", and the ping sample (with + LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom + returned the IP payload, without the IP header). + + 2008-03-04 Jonathan Larmour + * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors + and/or warnings on some systems where mem_size_t and size_t differ. + * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc. + + 2008-03-04 Kieran Mansley (contributions by others) + * Numerous small compiler error/warning fixes from contributions to + mailing list after 1.3.0 release candidate made. + + 2008-01-25 Cui hengbin (integrated by Frédéric Bernon) + * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures. + + 2008-01-15 Kieran Mansley + * tcp_out.c: BUG20511. Modify persist timer to start when we are + prevented from sending by a small send window, not just a zero + send window. + + 2008-01-09 Jonathan Larmour + * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid + conflict with Linux system headers. + + 2008-01-06 Jonathan Larmour + * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP + address entirely on receiving a DHCPNAK, and restarting discovery. + + 2007-12-21 Simon Goldschmidt + * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail + is not protected" by using new macros for interlocked access to modify/test + netconn->recv_avail. + + 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev) + * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state) + + 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling + of silly window avoidance and prevent lwIP from shrinking the window) + + 2007-12-04 Simon Goldschmidt + * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last + data packet was lost): add assert that all segment lists are empty in + tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED + state from LAST_ACK in tcp_process + + 2007-12-02 Simon Goldschmidt + * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET + If including for system-struct timeval, LWIP_TIMEVAL_PRIVATE now + has to be set to 0 in lwipopts.h + + 2007-12-02 Simon Goldschmidt + * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always + allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen + netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox. + This is a fix for thread-safety and allocates all items needed for a netconn + when the netconn is created. + + 2007-11-30 Simon Goldschmidt + * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple + netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed + to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same + port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address) + + 2007-11-27 Simon Goldschmidt + * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by + letting ip_route only use netifs that are up. + + 2007-11-27 Simon Goldschmidt + * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF + and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and + sockets block most operations once they have seen a fatal error. + + 2007-11-27 Simon Goldschmidt + * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the + netif to send as an argument (to be able to send on netifs that are down). + + 2007-11-26 Simon Goldschmidt + * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs + arrive out-of-order + + 2007-11-21 Simon Goldschmidt + * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early + Fixed the nagle algorithm; nagle now also works for all raw API applications + and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY' + + 2007-11-12 Frédéric Bernon + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most + of the netconn_peer and netconn_addr processing is done inside tcpip_thread + context in do_getaddr. + + 2007-11-10 Simon Goldschmidt + * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can + happen any time). Now the packet simply isn't enqueued when out of memory. + + 2007-11-01 Simon Goldschmidt + * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or + TCP_MSS if that is smaller) as long as no MSS option is received from the + remote host. + + 2007-11-01 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN) + is now based on TCP_MSS instead of pcb->mss (on passive open now effectively + sending our configured TCP_MSS instead of the one received). + + 2007-11-01 Simon Goldschmidt + * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was + calculated based on the configured TCP_MSS, not on the MSS option received + with SYN+ACK. + + 2007-10-09 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too + short and also was generated wrong if checksum coverage != tot_len; + receive: checksum was calculated wrong if checksum coverage != tot_len + + 2007-10-08 Simon Goldschmidt + * mem.c: lfree was not updated in mem_realloc! + + 2007-10-07 Frédéric Bernon + * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential + crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: + this change cause an API breakage for netconn_addr, since a parameter + type change. Any compiler should cause an error without any changes in + yours netconn_peer calls (so, it can't be a "silent change"). It also + reduce a little bit the footprint for socket layer (lwip_getpeername & + lwip_getsockname use now a common lwip_getaddrname function since + netconn_peer & netconn_addr have the same parameters). + + 2007-09-20 Simon Goldschmidt + * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state) + by checking tcp_tw_pcbs also + + 2007-09-19 Simon Goldschmidt + * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies) + + 2007-09-15 Mike Kleshov + * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used) + + 2007-09-06 Frédéric Bernon + * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove + it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which + already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h" + if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h. + + 2007-08-30 Frédéric Bernon + * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, + and fix some coding style. + + 2007-08-28 Frédéric Bernon + * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any + kind of packets. These packets are considered like Ethernet packets (payload + pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets + are considered like IP packets (payload pointing to iphdr). + + 2007-08-27 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error + problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state + and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT). + + 2007-08-24 Kieran Mansley + * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy + compiler (Paradigm C++) + + 2007-08-09 Frédéric Bernon, Bill Florac + * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement. + Introduce IGMP_STATS to centralize statistics management. + + 2007-08-09 Frédéric Bernon, Bill Florac + * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast + packet on a udp pcb binded on an netif's IP address, and not on "any". + + 2007-08-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement. + This is mainly on using lookup/lookfor, and some coding styles... + + 2007-07-26 Frédéric Bernon (and "thedoctor") + * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages. + + 2007-07-25 Simon Goldschmidt + * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if + tcp_output fails in tcp_close, the code in do_close_internal gets simpler + (tcp_output is called again later from tcp timers). + + 2007-07-25 Simon Goldschmidt + * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old + copy_from_pbuf, which illegally modified the given pbuf. + + 2007-07-25 Simon Goldschmidt + * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs: + changed snd_queuelen++ to snd_queuelen += pbuf_clen(p). + + 2007-07-24 Simon Goldschmidt + * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the + correct state (must be CLOSED). + + 2007-07-13 Thomas Taranowski (commited by Jared Grubb) + * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed + allocation. It now returns NULL. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in + all error cases. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed, + because current code doesn't follow rawapi.txt documentation. + + 2007-07-13 Kieran Mansley + * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in + out of sequence processing of received packets + + 2007-07-03 Simon Goldschmidt + * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an + assumption is made that this pbuf is in one piece (i.e. not chained). These + assumptions clash with the possibility of converting to fully pool-based + pbuf implementations, where PBUF_RAM pbufs might be chained. + + 2007-07-03 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems + when closing tcp netconns: removed conn->sem, less context switches when + closing, both netconn_close and netconn_delete should safely close tcp + connections. + + 2007-07-02 Simon Goldschmidt + * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c, + tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off) + to cache ARP table indices with each pcb instead of single-entry cache for + the complete stack. + + 2007-07-02 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent + warnings when assigning to smaller types. + + 2007-06-28 Simon Goldschmidt + * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing. + + 2007-06-28 Simon Goldschmidt + * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if + a segment contained chained pbufs) + + 2007-06-28 Frédéric Bernon + * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute + a "pseudo-random" value based on netif's MAC and some autoip fields. It's always + possible to define this macro in your own lwipopts.h to always use C library's + rand(). Note that autoip_create_rand_addr doesn't use this macro. + + 2007-06-28 Frédéric Bernon + * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option + LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications + in api_lib/api_msg (use pointers and not type with table, etc...) + + 2007-06-26 Simon Goldschmidt + * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload + for udp packets with no matching pcb. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match + could get udp input packets if the remote side matched. + + 2007-06-13 Simon Goldschmidt + * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get + changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0. + + 2007-06-13 Simon Goldschmidt + * api_msg.c: pcb_new sets conn->err if protocol is not implemented + -> netconn_new_..() does not allocate a new connection for unsupported + protocols. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * api_lib.c: change return expression in netconn_addr and netconn_peer, because + conn->err was reset to ERR_OK without any reasons (and error was lost)... + + 2007-06-13 Frédéric Bernon, Matthias Weisser + * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename + MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid + some macro names collision with some OS macros. + + 2007-06-11 Simon Goldschmidt + * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0, + create checksum over the complete packet. On RX, if it's < 8 (and not 0), + discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both + UDP & UDP Lite. + + 2007-06-11 Srinivas Gollakota & Oleg Tyshev + * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags" + where TCP flags wasn't initialized in tcp_keepalive. + + 2007-06-03 Simon Goldschmidt + * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function + registered, p->payload was modified without modifying p->len if sending + icmp_dest_unreach() (had no negative effect but was definitively wrong). + + 2007-06-03 Simon Goldschmidt + * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp + re-used the input pbuf even if that didn't have enough space to include the + link headers. Now the space is tested and a new pbuf is allocated for the + echo response packet if the echo request pbuf isn't big enough. + + 2007-06-01 Simon Goldschmidt + * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread. + + 2007-05-23 Frédéric Bernon + * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only + allocated by do_listen if success) and netconn_accept errors handling. In + most of api_lib functions, we replace some errors checkings like "if (conn==NULL)" + by ASSERT, except for netconn_delete. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return + an error code if it's impossible to fetch a pbuf on a TCP connection (and not + directly close the recvmbox). + + 2007-05-22 Simon Goldschmidt + * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of + bound but unconnected (and non-listening) tcp_pcbs. + + 2007-05-22 Frédéric Bernon + * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only + used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of + sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features + like "sys_timeout" in their application threads. + + 2007-05-22 Frédéric Bernon + * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see + which parameters are used by which do_xxx function, and to avoid "misusing" + parameters (patch #5938). + + 2007-05-22 Simon Goldschmidt + * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938: + changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto + is only 8 bits wide. This affects the api, as there, the protocol was + u16_t, too. + + 2007-05-18 Simon Goldschmidt + * memp.c: addition to patch #5913: smaller pointer was returned but + memp_memory was the same size -> did not save memory. + + 2007-05-16 Simon Goldschmidt + * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns + != ERR_OK. + + 2007-05-16 Simon Goldschmidt + * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same + as the one of the netif used for sending to prevent sending from old + addresses after a netif address gets changed (partly fixes bug #3168). + + 2007-05-16 Frédéric Bernon + * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work + with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in + tcpip_init) because we have to be sure that network interfaces are already + added (mac filter is updated only in igmp_init for the moment). + + 2007-05-16 Simon Goldschmidt + * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls + into sys_arch_sem_wait calls to prevent timers from running while waiting + for the heap. This fixes bug #19167. + + 2007-05-13 Simon Goldschmidt + * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines + for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from + tcp.h to sockets.h. + + 2007-05-07 Simon Goldschmidt + * mem.c: Another attempt to fix bug #17922. + + 2007-05-04 Simon Goldschmidt + * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy() + implementation so that it can be reused (don't allocate the target + pbuf inside pbuf_copy()). + + 2007-05-04 Simon Goldschmidt + * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem + to save a little RAM (next pointer of memp is not used while not in pool). + + 2007-05-03 "maq" + * sockets.c: Fix ioctl FIONREAD when some data remains from last recv. + (patch #3574). + + 2007-04-23 Simon Goldschmidt + * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results + in NULL reference for incoming TCP packets". Loopif has to be configured + (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input() + (multithreading environments, e.g. netif->input() = tcpip_input()) or + putting packets on a list that is fed to the stack by calling loopif_poll() + (single-thread / NO_SYS / polling environment where e.g. + netif->input() = ip_input). + + 2007-04-17 Jonathan Larmour + * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold + the difference between two u16_t's. + * sockets.h: FD_SETSIZE needs to match number of sockets, which is + MEMP_NUM_NETCONN in sockets.c right now. + + 2007-04-12 Jonathan Larmour + * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580). + + 2007-04-12 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission + timer is reset to fix bug#19434, with help from Oleg Tyshev. + + 2007-04-11 Simon Goldschmidt + * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than + previously thought need to be copied (everything but PBUF_ROM!). Cleaned up + pbuf.c: removed functions no needed any more (by etharp). + + 2007-04-11 Kieran Mansley + * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix + "Constant is long" warnings with 16bit compilers. Contributed by + avatar@mmlab.cse.yzu.edu.tw + + 2007-04-05 Frédéric Bernon, Jonathan Larmour + * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on + the mailbox is active". Now, the post is only done during a connect, and do_send, + do_write and do_join_leave_group don't do anything if a previous error was signaled. + + 2007-04-03 Frédéric Bernon + * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output + packets. See patch #5834. + + 2007-03-30 Frédéric Bernon + * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add + missing pcb allocations checking (in do_bind, and for each raw_new). Fix style. + + 2007-03-30 Frédéric Bernon + * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with + others environment defines (these were too "generic"). + + 2007-03-28 Frédéric Bernon + * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call + result and can cause a crash. lwip_send now check netbuf_ref result. + + 2007-03-28 Simon Goldschmidt + * sockets.c Remove "#include " from sockets.c to avoid multiple + definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is + defined. This is the way it should have been already (looking at + doc/sys_arch.txt) + + 2007-03-28 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS + + IP and TCP headers *and* physical link headers + + 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov) + * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause + to send some garbage. It is not a definitive solution, but the patch does solve + the problem for most cases. + + 2007-03-22 Frédéric Bernon + * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used). + + 2007-03-22 Frédéric Bernon + * api_lib.c: somes resources couldn't be freed if there was errors during + netconn_new_with_proto_and_callback. + + 2007-03-22 Frédéric Bernon + * ethernetif.c: update netif->input calls to check return value. In older ports, + it's a good idea to upgrade them, even if before, there could be another problem + (access to an uninitialized mailbox). + + 2007-03-21 Simon Goldschmidt + * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed + by casting to unsigned). + + 2007-03-21 Frédéric Bernon + * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from + api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a + dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call. + Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a + faster and more reliable communication between api_lib and tcpip. + + 2007-03-21 Frédéric Bernon + * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0. + + 2007-03-21 Frédéric Bernon + * api_msg.c, igmp.c, igmp.h: Fix C++ style comments + + 2007-03-21 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS + + IP and TCP headers + + 2007-03-21 Kieran Mansley + * Fix all uses of pbuf_header to check the return value. In some + cases just assert if it fails as I'm not sure how to fix them, but + this is no worse than before when they would carry on regardless + of the failure. + + 2007-03-21 Kieran Mansley + * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and + comment out missing header include in icmp.c + + 2007-03-20 Frédéric Bernon + * memp.h, stats.c: Fix stats_display function where memp_names table wasn't + synchronized with memp.h. + + 2007-03-20 Frédéric Bernon + * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input, + tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with + network interfaces. Also fix a compiler warning. + + 2007-03-20 Kieran Mansley + * udp.c: Only try and use pbuf_header() to make space for headers if + not a ROM or REF pbuf. + + 2007-03-19 Frédéric Bernon + * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg() + and api_msg_post(). + + 2007-03-19 Frédéric Bernon + * Remove unimplemented "memp_realloc" function from memp.h. + + 2007-03-11 Simon Goldschmidt + * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused + memory corruption. + + 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov) + * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251 + (missing `const' qualifier in socket functions), to get more compatible to + standard POSIX sockets. + + 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov) + * sockets.c: Add asserts inside bind, connect and sendto to check input + parameters. Remove excessive set_errno() calls after get_socket(), because + errno is set inside of get_socket(). Move last sock_set_errno() inside + lwip_close. + + 2007-03-09 Simon Goldschmidt + * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory + was allocated too small. + + 2007-03-06 Simon Goldschmidt + * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect + the stack from concurrent access. + + 2007-03-06 Frédéric Bernon, Dmitry Potapov + * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy + call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input(). + + 2007-03-06 Simon Goldschmidt + * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files + if IP_FRAG == 0 and IP_REASSEMBLY == 0 + + 2007-03-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration + option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput. + Allow to do ARP processing for incoming packets inside tcpip_thread + (protecting ARP layer against concurrent access). You can also disable + old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0. + Older ports have to use tcpip_ethinput. + + 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov) + * err.h, err.c: fixed compiler warning "initialization dircards qualifiers + from pointer target type" + + 2007-03-05 Frédéric Bernon + * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES, + ETHARP_TRUST_IP_MAC, review SO_REUSE) + + 2007-03-04 Frédéric Bernon + * api_msg.c: Remove some compiler warnings : parameter "pcb" was never + referenced. + + 2007-03-04 Frédéric Bernon + * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from + Dmitry Potapov). + The api_msg struct stay on the stack (not moved to netconn struct). + + 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov) + * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if + SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available) + Also fixed cast warning in pbuf_alloc() + + 2007-03-04 Simon Goldschmidt + * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt + existing pbuf chain when enqueuing multiple pbufs to a pending ARP request + + 2007-03-03 Frédéric Bernon + * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;" + It is static, and never used in udp.c except udp_init(). + + 2007-03-02 Simon Goldschmidt + * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from + tcpip_thread() to tcpip_init(). This way, raw API connections can be + initialized before tcpip_thread is running (e.g. before OS is started) + + 2007-03-02 Frédéric Bernon + * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call + interval. + + 2007-02-28 Kieran Mansley + * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved + outside the region of the pbuf by pbuf_header() + + 2007-02-28 Kieran Mansley + * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero + when supplied timeout is also non-zero + +(STABLE-1.2.0) + + 2006-12-05 Leon Woestenberg + * CHANGELOG: Mention STABLE-1.2.0 release. + + ++ New features: + + 2006-12-01 Christiaan Simons + * mem.h, opt.h: Added MEM_LIBC_MALLOC option. + Note this is a workaround. Currently I have no other options left. + + 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour) + * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define + to include/lwip/opt.h. + * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL. + Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h. + * opt.h: Add above new options. + + 2006-08-18 Christiaan Simons + * tcp_{in,out}.c: added SNMP counters. + * ipv4/ip.c: added SNMP counters. + * ipv4/ip_frag.c: added SNMP counters. + + 2006-08-08 Christiaan Simons + * etharp.{c,h}: added etharp_find_addr() to read + (stable) ethernet/IP address pair from ARP table + + 2006-07-14 Christiaan Simons + * mib_structs.c: added + * include/lwip/snmp_structs.h: added + * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct + + 2006-07-06 Christiaan Simons + * snmp/asn1_{enc,dec}.c added + * snmp/mib2.c added + * snmp/msg_{in,out}.c added + * include/lwip/snmp_asn1.h added + * include/lwip/snmp_msg.h added + * doc/snmp_agent.txt added + + 2006-03-29 Christiaan Simons + * inet.c, inet.h: Added platform byteswap support. + Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and + optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros. + + ++ Bug fixes: + + 2006-11-30 Christiaan Simons + * dhcp.c: Fixed false triggers of request_timeout. + + 2006-11-28 Christiaan Simons + * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags. + + 2006-10-11 Christiaan Simons + * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h: + Partially accepted patch #5449 for ANSI C compatibility / build fixes. + * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol + identifier from 170 to 136 (bug #17574). + + 2006-10-10 Christiaan Simons + * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice. + + 2006-08-17 Christiaan Simons + * udp.c: Fixed bug #17200, added check for broadcast + destinations for PCBs bound to a unicast address. + + 2006-08-07 Christiaan Simons + * api_msg.c: Flushing TCP output in do_close() (bug #15926). + + 2006-06-27 Christiaan Simons + * api_msg.c: Applied patch for cold case (bug #11135). + In accept_function() ensure newconn->callback is always initialized. + + 2006-06-15 Christiaan Simons + * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748), + facilitate printing of mem_size_t and u16_t statistics. + + 2006-06-14 Christiaan Simons + * api_msg.c: Applied patch #5146 to handle allocation failures + in accept() by Kevin Lawson. + + 2006-05-26 Christiaan Simons + * api_lib.c: Removed conn->sem creation and destruction + from netconn_write() and added sys_sem_new to netconn_new_*. + +(STABLE-1_1_1) + + 2006-03-03 Christiaan Simons + * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap + access and added pbuf_alloc() return value checks. + + 2006-01-01 Leon Woestenberg + * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is + now handled by the checksum routine properly. + + 2006-02-27 Leon Woestenberg + * pbuf.c: Fix alignment; pbuf_init() would not work unless + pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.) + + 2005-12-20 Leon Woestenberg + * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch + submitted by Mitrani Hiroshi. + + 2005-12-15 Christiaan Simons + * inet.c: Disabled the added summing routine to preserve code space. + + 2005-12-14 Leon Woestenberg + * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson. + Added Curt McDowell's optimized checksumming routine for future + inclusion. Need to create test case for unaliged, aligned, odd, + even length combination of cases on various endianess machines. + + 2005-12-09 Christiaan Simons + * inet.c: Rewrote standard checksum routine in proper portable C. + + 2005-11-25 Christiaan Simons + * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only. + * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t, + u32_t, s32_t typedefs. This solves most debug word-length assumes. + + 2005-07-17 Leon Woestenberg + * inet.c: Fixed unaligned 16-bit access in the standard checksum + routine by Peter Jolasson. + * slipif.c: Fixed implementation assumption of single-pbuf datagrams. + + 2005-02-04 Leon Woestenberg + * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch. + * tcp_{out|in}.c: Applied patch fixing unaligned access. + + 2005-01-04 Leon Woestenberg + * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement. + + 2005-01-03 Leon Woestenberg + * udp.c: UDP pcb->recv() was called even when it was NULL. + +(STABLE-1_1_0) + + 2004-12-28 Leon Woestenberg + * etharp.*: Disabled multiple packets on the ARP queue. + This clashes with TCP queueing. + + 2004-11-28 Leon Woestenberg + * etharp.*: Fixed race condition from ARP request to ARP timeout. + Halved the ARP period, doubled the period counts. + ETHARP_MAX_PENDING now should be at least 2. This prevents + the counter from reaching 0 right away (which would allow + too little time for ARP responses to be received). + + 2004-11-25 Leon Woestenberg + * dhcp.c: Decline messages were not multicast but unicast. + * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD. + Do not try hard to insert arbitrary packet's source address, + etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. + etharp_query() now always DOES call ETHARP_TRY_HARD so that users + querying an address will see it appear in the cache (DHCP could + suffer from this when a server invalidly gave an in-use address.) + * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are + comparing network addresses (identifiers), not the network masks + themselves. + * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given + IP address actually belongs to the network of the given interface. + + 2004-11-24 Kieran Mansley + * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state. + +(STABLE-1_1_0-RC1) + + 2004-10-16 Kieran Mansley + * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately, + even if one is already pending, if the rcv_wnd is above a threshold + (currently TCP_WND/2). This avoids waiting for a timer to expire to send a + delayed ACK in order to open the window if the stack is only receiving data. + + 2004-09-12 Kieran Mansley + * tcp*.*: Retransmit time-out handling improvement by Sam Jansen. + + 2004-08-20 Tony Mountifield + * etharp.c: Make sure the first pbuf queued on an ARP entry + is properly ref counted. + + 2004-07-27 Tony Mountifield + * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler + warnings about comparison. + * pbuf.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. Closed an unclosed comment. + * tcp.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. + * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons(). + * inet.c: Added a couple of casts to quiet the compiler. + No need to test isascii(c) before isdigit(c) or isxdigit(c). + + 2004-07-22 Tony Mountifield + * inet.c: Made data types consistent in inet_ntoa(). + Added casts for return values of checksum routines, to pacify compiler. + * ip_frag.c, tcp_out.c, sockets.c, pbuf.c + Small corrections to some debugging statements, to pacify compiler. + + 2004-07-21 Tony Mountifield + * etharp.c: Removed spurious semicolon and added missing end-of-comment. + * ethernetif.c Updated low_level_output() to match prototype for + netif->linkoutput and changed low_level_input() similarly for consistency. + * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype + of raw_recv() in raw.h and so avoid compiler error. + * sockets.c: Added trivial (int) cast to keep compiler happier. + * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros. + +(STABLE-1_0_0) + + ++ Changes: + + 2004-07-05 Leon Woestenberg + * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure + your cc.h file defines this either 1 or 0. If non-defined, + defaults to 1. + * .c: Added and includes where used. + * etharp.c: Made some array indices unsigned. + + 2004-06-27 Leon Woestenberg + * netif.*: Added netif_set_up()/down(). + * dhcp.c: Changes to restart program flow. + + 2004-05-07 Leon Woestenberg + * etharp.c: In find_entry(), instead of a list traversal per candidate, do a + single-pass lookup for different candidates. Should exploit locality. + + 2004-04-29 Leon Woestenberg + * tcp*.c: Cleaned up source comment documentation for Doxygen processing. + * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC. + * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by + the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option. + + ++ Bug fixes: + + 2004-04-27 Leon Woestenberg + * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution + suggested by Timmy Brolin. Fix for 32-bit processors that cannot access + non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix + is to prefix the 14-bit Ethernet headers with two padding bytes. + + 2004-04-23 Leon Woestenberg + * ip_addr.c: Fix in the ip_addr_isbroadcast() check. + * etharp.c: Fixed the case where the packet that initiates the ARP request + is not queued, and gets lost. Fixed the case where the packets destination + address is already known; we now always queue the packet and perform an ARP + request. + +(STABLE-0_7_0) + + ++ Bug fixes: + + * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition. + * Fixed TCP bug in dequeueing of FIN from out of order segment queue. + * Fixed two possible NULL references in rare cases. + +(STABLE-0_6_6) + + ++ Bug fixes: + + * Fixed DHCP which did not include the IP address in DECLINE messages. + + ++ Changes: + + * etharp.c has been hauled over a bit. + +(STABLE-0_6_5) + + ++ Bug fixes: + + * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic. + * Packets sent from ARP queue had invalid source hardware address. + + ++ Changes: + + * Pass-by ARP requests do now update the cache. + + ++ New features: + + * No longer dependent on ctype.h. + * New socket options. + * Raw IP pcb support. + +(STABLE-0_6_4) + + ++ Bug fixes: + + * Some debug formatters and casts fixed. + * Numereous fixes in PPP. + + ++ Changes: + + * DEBUGF now is LWIP_DEBUGF + * pbuf_dechain() has been re-enabled. + * Mentioned the changed use of CVS branches in README. + +(STABLE-0_6_3) + + ++ Bug fixes: + + * Fixed pool pbuf memory leak in pbuf_alloc(). + Occured if not enough PBUF_POOL pbufs for a packet pbuf chain. + Reported by Savin Zlobec. + + * PBUF_POOL chains had their tot_len field not set for non-first + pbufs. Fixed in pbuf_alloc(). + + ++ New features: + + * Added PPP stack contributed by Marc Boucher + + ++ Changes: + + * Now drops short packets for ICMP/UDP/TCP protocols. More robust. + + * ARP queueuing now queues the latest packet instead of the first. + This is the RFC recommended behaviour, but can be overridden in + lwipopts.h. + +(0.6.2) + + ++ Bugfixes: + + * TCP has been fixed to deal with the new use of the pbuf->ref + counter. + + * DHCP dhcp_inform() crash bug fixed. + + ++ Changes: + + * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed + pbuf_refresh(). This has sped up pbuf pool operations considerably. + Implemented by David Haas. + +(0.6.1) + + ++ New features: + + * The packet buffer implementation has been enhanced to support + zero-copy and copy-on-demand for packet buffers which have their + payloads in application-managed memory. + Implemented by David Haas. + + Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy + if an outgoing packet can be directly sent on the link, or perform + a copy-on-demand when necessary. + + The application can safely assume the packet is sent, and the RAM + is available to the application directly after calling udp_send() + or similar function. + + ++ Bugfixes: + + * ARP_QUEUEING should now correctly work for all cases, including + PBUF_REF. + Implemented by Leon Woestenberg. + + ++ Changes: + + * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer + to a '0.0.0.0' IP address. + + * The packet buffer implementation is changed. The pbuf->ref counter + meaning has changed, and several pbuf functions have been + adapted accordingly. + + * netif drivers have to be changed to set the hardware address length field + that must be initialized correctly by the driver (hint: 6 for Ethernet MAC). + See the contrib/ports/c16x cs8900 driver as a driver example. + + * netif's have a dhcp field that must be initialized to NULL by the driver. + See the contrib/ports/c16x cs8900 driver as a driver example. + +(0.5.x) This file has been unmaintained up to 0.6.1. All changes are + logged in CVS but have not been explained here. + +(0.5.3) Changes since version 0.5.2 + + ++ Bugfixes: + + * memp_malloc(MEMP_API_MSG) could fail with multiple application + threads because it wasn't protected by semaphores. + + ++ Other changes: + + * struct ip_addr now packed. + + * The name of the time variable in arp.c has been changed to ctime + to avoid conflicts with the time() function. + +(0.5.2) Changes since version 0.5.1 + + ++ New features: + + * A new TCP function, tcp_tmr(), now handles both TCP timers. + + ++ Bugfixes: + + * A bug in tcp_parseopt() could cause the stack to hang because of a + malformed TCP option. + + * The address of new connections in the accept() function in the BSD + socket library was not handled correctly. + + * pbuf_dechain() did not update the ->tot_len field of the tail. + + * Aborted TCP connections were not handled correctly in all + situations. + + ++ Other changes: + + * All protocol header structs are now packed. + + * The ->len field in the tcp_seg structure now counts the actual + amount of data, and does not add one for SYN and FIN segments. + +(0.5.1) Changes since version 0.5.0 + + ++ New features: + + * Possible to run as a user process under Linux. + + * Preliminary support for cross platform packed structs. + + * ARP timer now implemented. + + ++ Bugfixes: + + * TCP output queue length was badly initialized when opening + connections. + + * TCP delayed ACKs were not sent correctly. + + * Explicit initialization of BSS segment variables. + + * read() in BSD socket library could drop data. + + * Problems with memory alignment. + + * Situations when all TCP buffers were used could lead to + starvation. + + * TCP MSS option wasn't parsed correctly. + + * Problems with UDP checksum calculation. + + * IP multicast address tests had endianess problems. + + * ARP requests had wrong destination hardware address. + + ++ Other changes: + + * struct eth_addr changed from u16_t[3] array to u8_t[6]. + + * A ->linkoutput() member was added to struct netif. + + * TCP and UDP ->dest_* struct members where changed to ->remote_*. + + * ntoh* macros are now null definitions for big endian CPUs. + +(0.5.0) Changes since version 0.4.2 + + ++ New features: + + * Redesigned operating system emulation layer to make porting easier. + + * Better control over TCP output buffers. + + * Documenation added. + + ++ Bugfixes: + + * Locking issues in buffer management. + + * Bugfixes in the sequential API. + + * IP forwarding could cause memory leakage. This has been fixed. + + ++ Other changes: + + * Directory structure somewhat changed; the core/ tree has been + collapsed. + +(0.4.2) Changes since version 0.4.1 + + ++ New features: + + * Experimental ARP implementation added. + + * Skeleton Ethernet driver added. + + * Experimental BSD socket API library added. + + ++ Bugfixes: + + * In very intense situations, memory leakage could occur. This has + been fixed. + + ++ Other changes: + + * Variables named "data" and "code" have been renamed in order to + avoid name conflicts in certain compilers. + + * Variable++ have in appliciable cases been translated to ++variable + since some compilers generate better code in the latter case. + +(0.4.1) Changes since version 0.4 + + ++ New features: + + * TCP: Connection attempts time out earlier than data + transmissions. Nagle algorithm implemented. Push flag set on the + last segment in a burst. + + * UDP: experimental support for UDP-Lite extensions. + + ++ Bugfixes: + + * TCP: out of order segments were in some cases handled incorrectly, + and this has now been fixed. Delayed acknowledgements was broken + in 0.4, has now been fixed. Binding to an address that is in use + now results in an error. Reset connections sometimes hung an + application; this has been fixed. + + * Checksum calculation sometimes failed for chained pbufs with odd + lengths. This has been fixed. + + * API: a lot of bug fixes in the API. The UDP API has been improved + and tested. Error reporting and handling has been + improved. Logical flaws and race conditions for incoming TCP + connections has been found and removed. + + * Memory manager: alignment issues. Reallocating memory sometimes + failed, this has been fixed. + + * Generic library: bcopy was flawed and has been fixed. + + ++ Other changes: + + * API: all datatypes has been changed from generic ones such as + ints, to specified ones such as u16_t. Functions that return + errors now have the correct type (err_t). + + * General: A lot of code cleaned up and debugging code removed. Many + portability issues have been fixed. + + * The license was changed; the advertising clause was removed. + + * C64 port added. + + * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri + Kosunen, Mikael Caleres, and Frits Wilmink for reporting and + fixing bugs! + +(0.4) Changes since version 0.3.1 + + * Memory management has been radically changed; instead of + allocating memory from a shared heap, memory for objects that are + rapidly allocated and deallocated is now kept in pools. Allocation + and deallocation from those memory pools is very fast. The shared + heap is still present but is used less frequently. + + * The memory, memory pool, and packet buffer subsystems now support + 4-, 2-, or 1-byte alignment. + + * "Out of memory" situations are handled in a more robust way. + + * Stack usage has been reduced. + + * Easier configuration of lwIP parameters such as memory usage, + TTLs, statistics gathering, etc. All configuration parameters are + now kept in a single header file "lwipopts.h". + + * The directory structure has been changed slightly so that all + architecture specific files are kept under the src/arch + hierarchy. + + * Error propagation has been improved, both in the protocol modules + and in the API. + + * The code for the RTXC architecture has been implemented, tested + and put to use. + + * Bugs have been found and corrected in the TCP, UDP, IP, API, and + the Internet checksum modules. + + * Bugs related to porting between a 32-bit and a 16-bit architecture + have been found and corrected. + + * The license has been changed slightly to conform more with the + original BSD license, including the advertisement clause. + +(0.3.1) Changes since version 0.3 + + * Fix of a fatal bug in the buffer management. Pbufs with allocated + RAM never returned the RAM when the pbuf was deallocated. + + * TCP congestion control, window updates and retransmissions did not + work correctly. This has now been fixed. + + * Bugfixes in the API. + +(0.3) Changes since version 0.2 + + * New and improved directory structure. All include files are now + kept in a dedicated include/ directory. + + * The API now has proper error handling. A new function, + netconn_err(), now returns an error code for the connection in + case of errors. + + * Improvements in the memory management subsystem. The system now + keeps a pointer to the lowest free memory block. A new function, + mem_malloc2() tries to allocate memory once, and if it fails tries + to free some memory and retry the allocation. + + * Much testing has been done with limited memory + configurations. lwIP now does a better job when overloaded. + + * Some bugfixes and improvements to the buffer (pbuf) subsystem. + + * Many bugfixes in the TCP code: + + - Fixed a bug in tcp_close(). + + - The TCP receive window was incorrectly closed when out of + sequence segments was received. This has been fixed. + + - Connections are now timed-out of the FIN-WAIT-2 state. + + - The initial congestion window could in some cases be too + large. This has been fixed. + + - The retransmission queue could in some cases be screwed up. This + has been fixed. + + - TCP RST flag now handled correctly. + + - Out of sequence data was in some cases never delivered to the + application. This has been fixed. + + - Retransmitted segments now contain the correct acknowledgment + number and advertised window. + + - TCP retransmission timeout backoffs are not correctly computed + (ala BSD). After a number of retransmissions, TCP now gives up + the connection. + + * TCP connections now are kept on three lists, one for active + connections, one for listening connections, and one for + connections that are in TIME-WAIT. This greatly speeds up the fast + timeout processing for sending delayed ACKs. + + * TCP now provides proper feedback to the application when a + connection has been successfully set up. + + * More comments have been added to the code. The code has also been + somewhat cleaned up. + +(0.2) Initial public release. diff --git a/external/badvpn_dns/lwip/CMakeLists.txt b/external/badvpn_dns/lwip/CMakeLists.txt new file mode 100644 index 00000000..121892d3 --- /dev/null +++ b/external/badvpn_dns/lwip/CMakeLists.txt @@ -0,0 +1,27 @@ +set(LWIP_SOURCES + src/core/timers.c + src/core/udp.c + src/core/memp.c + src/core/init.c + src/core/pbuf.c + src/core/tcp.c + src/core/tcp_out.c + src/core/sys.c + src/core/netif.c + src/core/def.c + src/core/mem.c + src/core/tcp_in.c + src/core/stats.c + src/core/inet_chksum.c + src/core/ipv4/icmp.c + src/core/ipv4/ip4.c + src/core/ipv4/ip4_addr.c + src/core/ipv4/ip_frag.c + src/core/ipv6/ip6.c + src/core/ipv6/nd6.c + src/core/ipv6/icmp6.c + src/core/ipv6/ip6_addr.c + src/core/ipv6/ip6_frag.c + custom/sys.c +) +badvpn_add_library(lwip "system" "" "${LWIP_SOURCES}") diff --git a/external/badvpn_dns/lwip/COPYING b/external/badvpn_dns/lwip/COPYING new file mode 100644 index 00000000..e23898b5 --- /dev/null +++ b/external/badvpn_dns/lwip/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + diff --git a/external/badvpn_dns/lwip/FILES b/external/badvpn_dns/lwip/FILES new file mode 100644 index 00000000..66253196 --- /dev/null +++ b/external/badvpn_dns/lwip/FILES @@ -0,0 +1,4 @@ +src/ - The source code for the lwIP TCP/IP stack. +doc/ - The documentation for lwIP. + +See also the FILES file in each subdirectory. diff --git a/external/badvpn_dns/lwip/README b/external/badvpn_dns/lwip/README new file mode 100644 index 00000000..a62cc4f3 --- /dev/null +++ b/external/badvpn_dns/lwip/README @@ -0,0 +1,89 @@ +INTRODUCTION + +lwIP is a small independent implementation of the TCP/IP protocol +suite that has been developed by Adam Dunkels at the Computer and +Networks Architectures (CNA) lab at the Swedish Institute of Computer +Science (SICS). + +The focus of the lwIP TCP/IP implementation is to reduce the RAM usage +while still having a full scale TCP. This making lwIP suitable for use +in embedded systems with tens of kilobytes of free RAM and room for +around 40 kilobytes of code ROM. + +FEATURES + + * IP (Internet Protocol) including packet forwarding over multiple network + interfaces + * ICMP (Internet Control Message Protocol) for network maintenance and debugging + * IGMP (Internet Group Management Protocol) for multicast traffic management + * UDP (User Datagram Protocol) including experimental UDP-lite extensions + * TCP (Transmission Control Protocol) with congestion control, RTT estimation + and fast recovery/fast retransmit + * Specialized raw/native API for enhanced performance + * Optional Berkeley-like socket API + * DNS (Domain names resolver) + * SNMP (Simple Network Management Protocol) + * DHCP (Dynamic Host Configuration Protocol) + * AUTOIP (for IPv4, conform with RFC 3927) + * PPP (Point-to-Point Protocol) + * ARP (Address Resolution Protocol) for Ethernet + +LICENSE + +lwIP is freely available under a BSD license. + +DEVELOPMENT + +lwIP has grown into an excellent TCP/IP stack for embedded devices, +and developers using the stack often submit bug fixes, improvements, +and additions to the stack to further increase its usefulness. + +Development of lwIP is hosted on Savannah, a central point for +software development, maintenance and distribution. Everyone can +help improve lwIP by use of Savannah's interface, CVS and the +mailing list. A core team of developers will commit changes to the +CVS source tree. + +The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and +contributions (such as platform ports) are in the 'contrib' module. + +See doc/savannah.txt for details on CVS server access for users and +developers. + +Last night's CVS tar ball can be downloaded from: + http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING] + +The current CVS trees are web-browsable: + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/ + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/ + +Submit patches and bugs via the lwIP project page: + http://savannah.nongnu.org/projects/lwip/ + + +DOCUMENTATION + +The original out-dated homepage of lwIP and Adam Dunkels' papers on +lwIP are at the official lwIP home page: + http://www.sics.se/~adam/lwip/ + +Self documentation of the source code is regularly extracted from the +current CVS sources and is available from this web page: + http://www.nongnu.org/lwip/ + +There is now a constantly growin wiki about lwIP at + http://lwip.wikia.com/wiki/LwIP_Wiki + +Also, there are mailing lists you can subscribe at + http://savannah.nongnu.org/mail/?group=lwip +plus searchable archives: + http://lists.nongnu.org/archive/html/lwip-users/ + http://lists.nongnu.org/archive/html/lwip-devel/ + +Reading Adam's papers, the files in docs/, browsing the source code +documentation and browsing the mailing list archives is a good way to +become familiar with the design of lwIP. + +Adam Dunkels +Leon Woestenberg + diff --git a/external/badvpn_dns/lwip/UPGRADING b/external/badvpn_dns/lwip/UPGRADING new file mode 100644 index 00000000..6501107a --- /dev/null +++ b/external/badvpn_dns/lwip/UPGRADING @@ -0,0 +1,144 @@ +This file lists major changes between release versions that require +ports or applications to be changed. Use it to update a port or an +application written for an older version of lwIP to correctly work +with newer versions. + + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ Application changes: + + * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for + compatibility to old applications, but will be removed in the future). + + * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc() + + +++ Raw API: + * Changed the semantics of tcp_close() (since it was rather a + shutdown before): Now the application does *NOT* get any calls to the recv + callback (aside from NULL/closed) after calling tcp_close() + + * When calling tcp_abort() from a raw API TCP callback function, + make sure you return ERR_ABRT to prevent accessing unallocated memory. + (ERR_ABRT now means the applicaiton has called tcp_abort!) + + +++ Netconn API: + * Changed netconn_receive() and netconn_accept() to return + err_t, not a pointer to new data/netconn. + + +++ Socket API: + * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they + now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT. + + * Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + +++ all APIs: + * correctly implemented SO(F)_REUSEADDR + + ++ Port changes + + +++ new files: + + * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h: + + * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains + the actual application programmer's API + + * Separated timer implementation from sys.h/.c, moved to timers.h/.c; + Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you + still want to use your own timer implementation for NO_SYS==0 (as before). + + +++ sys layer: + + * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/ + sys_sem_t; + + * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + + * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use + binary semaphores instead of mutexes - as before) + + +++ new options: + + * Don't waste memory when chaining segments, added option TCP_OVERSIZE to + prevent creating many small pbufs when calling tcp_write with many small + blocks of data. Instead, pbufs are allocated larger than needed and the + space is used for later calls to tcp_write. + + * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs + in tcp_write/udp_send. + + * Added an additional option LWIP_ETHERNET to support ethernet without ARP + (necessary for pure PPPoE) + + * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may + be used to place these pools into user-defined memory by using external + declaration. + + * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT + + +++ new pools: + + * Netdb uses a memp pool for allocating memory when getaddrinfo() is called, + so MEMP_NUM_NETDB has to be set accordingly. + + * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so + MEMP_NUM_LOCALHOSTLIST has to be set accordingly. + + * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have + to be set accordingly. + + * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES + has to be set accordingly + + * Integrated loopif into netif.c - loopif does not have to be created by the + port any more, just define LWIP_HAVE_LOOPIF to 1. + + * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined + in cc.h, e.g. used by igmp) + + * Added printf-formatter X8_F to printf u8_t as hex + + * The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + + * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work + with user-allocated structs instead of calling mem_malloc + + * Added const char* name to mem- and memp-stats for easier debugging. + + * Calculate the TCP/UDP checksum while copying to only fetch data once: + Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum + + * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to + more than one pcb. + + * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned + off any more, if this is set to 0, only one packet (the most recent one) is + queued (like demanded by RFC 1122). + + + ++ Major bugfixes/improvements + + * Implemented tcp_shutdown() to only shut down one end of a connection + * Implemented shutdown() at socket- and netconn-level + * Added errorset support to select() + improved select speed overhead + * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x) + * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1 + * Use macros defined in ip_addr.h to work with IP addresses + * Implemented many nonblocking socket/netconn functions + * Fixed ARP input processing: only add a new entry if a request was directed as us + * mem_realloc() to mem_trim() to prevent confusion with realloc() + * Some improvements for AutoIP (don't route/forward link-local addresses, don't break + existing connections when assigning a routable address) + * Correctly handle remote side overrunning our rcv_wnd in ooseq case + * Removed packing from ip_addr_t, the packed version is now only used in protocol headers + * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0 + * Added support for static ARP table entries + +(STABLE-1.3.2) + + * initial version of this file diff --git a/external/badvpn_dns/lwip/custom/arch/cc.h b/external/badvpn_dns/lwip/custom/arch/cc.h new file mode 100644 index 00000000..653a2e23 --- /dev/null +++ b/external/badvpn_dns/lwip/custom/arch/cc.h @@ -0,0 +1,96 @@ +/** + * @file cc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_CC_H +#define LWIP_CUSTOM_CC_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define u8_t uint8_t +#define s8_t int8_t +#define u16_t uint16_t +#define s16_t int16_t +#define u32_t uint32_t +#define s32_t int32_t +#define mem_ptr_t uintptr_t + +#define PACK_STRUCT_BEGIN B_START_PACKED +#define PACK_STRUCT_END B_END_PACKED +#define PACK_STRUCT_STRUCT B_PACKED + +#define LWIP_PLATFORM_DIAG(x) { if (BLog_WouldLog(BLOG_CHANNEL_lwip, BLOG_INFO)) { BLog_Begin(); BLog_Append x; BLog_Finish(BLOG_CHANNEL_lwip, BLOG_INFO); } } +#define LWIP_PLATFORM_ASSERT(x) { fprintf(stderr, "%s: lwip assertion failure: %s\n", __FUNCTION__, (x)); abort(); } + +#define U16_F PRIu16 +#define S16_F PRId16 +#define X16_F PRIx16 +#define U32_F PRIu32 +#define S32_F PRId32 +#define X32_F PRIx32 +#define SZT_F "zu" + +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) hton16(x) +#define LWIP_PLATFORM_HTONL(x) hton32(x) + +#define LWIP_RAND() ( \ + (((uint32_t)(rand() & 0xFF)) << 24) | \ + (((uint32_t)(rand() & 0xFF)) << 16) | \ + (((uint32_t)(rand() & 0xFF)) << 8) | \ + (((uint32_t)(rand() & 0xFF)) << 0) \ +) + +// for BYTE_ORDER +#if defined(BADVPN_USE_WINAPI) && !defined(_MSC_VER) + #include +#elif defined(BADVPN_LINUX) + #include +#elif defined(BADVPN_FREEBSD) + #include +#else + #define LITTLE_ENDIAN 1234 + #define BIG_ENDIAN 4321 + #if defined(BADVPN_LITTLE_ENDIAN) + #define BYTE_ORDER LITTLE_ENDIAN + #else + #define BYTE_ORDER BIG_ENDIAN + #endif +#endif + +#endif diff --git a/external/badvpn_dns/lwip/custom/arch/perf.h b/external/badvpn_dns/lwip/custom/arch/perf.h new file mode 100644 index 00000000..09c9d47d --- /dev/null +++ b/external/badvpn_dns/lwip/custom/arch/perf.h @@ -0,0 +1,36 @@ +/** + * @file perf.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_PERF_H +#define LWIP_CUSTOM_PERF_H + +#define PERF_START +#define PERF_STOP(x) + +#endif diff --git a/external/badvpn_dns/lwip/custom/lwipopts.h b/external/badvpn_dns/lwip/custom/lwipopts.h new file mode 100644 index 00000000..64e03ec0 --- /dev/null +++ b/external/badvpn_dns/lwip/custom/lwipopts.h @@ -0,0 +1,70 @@ +/** + * @file lwipopts.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LWIP_CUSTOM_LWIPOPTS_H +#define LWIP_CUSTOM_LWIPOPTS_H + +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_ARP 0 +#define ARP_QUEUEING 0 +#define IP_FORWARD 0 +#define LWIP_ICMP 1 +#define LWIP_RAW 0 +#define LWIP_DHCP 0 +#define LWIP_AUTOIP 0 +#define LWIP_SNMP 0 +#define LWIP_IGMP 0 +#define LWIP_DNS 0 +#define LWIP_UDP 0 +#define LWIP_UDPLITE 0 +#define LWIP_TCP 1 +#define LWIP_CALLBACK_API 1 +#define LWIP_NETIF_API 0 +#define LWIP_NETIF_LOOPBACK 0 +#define LWIP_HAVE_LOOPIF 0 +#define LWIP_HAVE_SLIPIF 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define PPP_SUPPORT 0 +#define LWIP_IPV6 1 +#define LWIP_IPV6_MLD 0 +#define LWIP_IPV6_AUTOCONFIG 0 + +#define MEMP_NUM_TCP_PCB_LISTEN 16 +#define MEMP_NUM_TCP_PCB 1024 +#define TCP_MSS 1460 +#define TCP_SND_BUF 16384 +#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF)/(TCP_MSS)) + +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 + +#endif diff --git a/external/badvpn_dns/lwip/custom/sys.c b/external/badvpn_dns/lwip/custom/sys.c new file mode 100644 index 00000000..efd14559 --- /dev/null +++ b/external/badvpn_dns/lwip/custom/sys.c @@ -0,0 +1,37 @@ +/** + * @file sys.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +u32_t sys_now (void) +{ + return btime_gettime(); +} diff --git a/external/badvpn_dns/lwip/doc/FILES b/external/badvpn_dns/lwip/doc/FILES new file mode 100644 index 00000000..05d356f4 --- /dev/null +++ b/external/badvpn_dns/lwip/doc/FILES @@ -0,0 +1,6 @@ +savannah.txt - How to obtain the current development source code. +contrib.txt - How to contribute to lwIP as a developer. +rawapi.txt - The documentation for the core API of lwIP. + Also provides an overview about the other APIs and multithreading. +snmp_agent.txt - The documentation for the lwIP SNMP agent. +sys_arch.txt - The documentation for a system abstraction layer of lwIP. diff --git a/external/badvpn_dns/lwip/doc/contrib.txt b/external/badvpn_dns/lwip/doc/contrib.txt new file mode 100644 index 00000000..39596fca --- /dev/null +++ b/external/badvpn_dns/lwip/doc/contrib.txt @@ -0,0 +1,63 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two) + can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded + as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead + for reporting a compiler warning fix. +6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/external/badvpn_dns/lwip/doc/rawapi.txt b/external/badvpn_dns/lwip/doc/rawapi.txt new file mode 100644 index 00000000..8c190305 --- /dev/null +++ b/external/badvpn_dns/lwip/doc/rawapi.txt @@ -0,0 +1,511 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed + into the accept callback! + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The apiflags can be one or more of: + - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated + for the data to be copied into. If this flag is not given, no new memory + should be allocated and the data should only be referenced by pointer. This + also means that the memory behind dataptr must not change until the data is + ACKed by the remote host + - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given, + the PSH flag is set in the last segment created by this call to tcp_write. + If this flag is given, the PSH flag is not set. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct tcp_pcb *pcb) + + Aborts the connection by sending a RST (reset) segment to the remote + host. The pcb is deallocated. This function never fails. + + ATTENTION: When calling this from one of the TCP callbacks, make + sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + or you will risk accessing deallocated memory or memory leaks! + + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + ip_addr_t *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, ip_addr_t *ipaddr, + ip_addr_t *netmask, ip_addr_t *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. + +For more optimization hints take a look at the lwIP wiki. + +--- Zero-copy MACs + +To achieve zero-copy on transmit, the data passed to the raw API must +remain unchanged until sent. Because the send- (or write-)functions return +when the packets have been enqueued for sending, data must be kept stable +after that, too. + +This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions +must *not* be reused by the application unless their ref-count is 1. + +For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too, +but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while +PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change). + +Also, data passed to tcp_write without the copy-flag must not be changed! + +Therefore, be careful which type of PBUF you use and if you copy TCP data +or not! diff --git a/external/badvpn_dns/lwip/doc/savannah.txt b/external/badvpn_dns/lwip/doc/savannah.txt new file mode 100644 index 00000000..409905b1 --- /dev/null +++ b/external/badvpn_dns/lwip/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/external/badvpn_dns/lwip/doc/snmp_agent.txt b/external/badvpn_dns/lwip/doc/snmp_agent.txt new file mode 100644 index 00000000..2653230f --- /dev/null +++ b/external/badvpn_dns/lwip/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads than the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for a dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/external/badvpn_dns/lwip/doc/sys_arch.txt b/external/badvpn_dns/lwip/doc/sys_arch.txt new file mode 100644 index 00000000..847cd777 --- /dev/null +++ b/external/badvpn_dns/lwip/doc/sys_arch.txt @@ -0,0 +1,267 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that +allows both using pointers or actual OS structures to be used. This way, memory +required for such types can be either allocated in place (globally or on the +stack) or on the heap (allocated internally in the "*_new()" functions). + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- err_t sys_sem_new(sys_sem_t *sem, u8_t count) + + Creates a new semaphore. The semaphore is allocated to the memory that 'sem' + points to (which can be both a pointer or the actual OS structure). + The "count" argument specifies the initial state of the semaphore (which is + either 0 or 1). + If the semaphore has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_sem_free(sys_sem_t *sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t *sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- int sys_sem_valid(sys_sem_t *sem) + + Returns 1 if the semaphore is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_sem_set_invalid(sys_sem_t *sem) + + Invalidate a semaphore so that sys_sem_valid() returns 0. + ATTENTION: This does NOT mean that the semaphore shall be deallocated: + sys_sem_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +- err_t sys_mbox_new(sys_mbox_t *mbox, int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + If the mailbox has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_mbox_free(sys_mbox_t *mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t *mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- int sys_mbox_valid(sys_mbox_t *mbox) + + Returns 1 if the mailbox is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_mbox_set_invalid(sys_mbox_t *mbox) + + Invalidate a mailbox so that sys_mbox_valid() returns 0. + ATTENTION: This does NOT mean that the mailbox shall be deallocated: + sys_mbox_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +For some configurations, you also need: + +- u32_t sys_now(void) + + This optional function returns the current time in milliseconds (don't care + for wraparound, this is only used for time diffs). + Not implementing this function means you cannot use some modules (e.g. TCP + timestamps, internal timeouts for NO_SYS==1). + + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/external/badvpn_dns/lwip/lwip-base-version b/external/badvpn_dns/lwip/lwip-base-version new file mode 100644 index 00000000..b48d5b6d --- /dev/null +++ b/external/badvpn_dns/lwip/lwip-base-version @@ -0,0 +1 @@ +666e84eef281d0059377d0f5029c1c550488f829 diff --git a/external/badvpn_dns/lwip/src/FILES b/external/badvpn_dns/lwip/src/FILES new file mode 100644 index 00000000..952aeabb --- /dev/null +++ b/external/badvpn_dns/lwip/src/FILES @@ -0,0 +1,13 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here, + as well as the ARP module. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/external/badvpn_dns/lwip/src/api/api_lib.c b/external/badvpn_dns/lwip/src/api/api_lib.c new file mode 100644 index 00000000..adaaad43 --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/api_lib.c @@ -0,0 +1,788 @@ +/** + * @file + * Sequential API External module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* This is the part of the API that is linked with + the application */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" + +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is also created. + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) +{ + struct netconn *conn; + struct api_msg msg; + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + err_t err; + msg.msg.msg.n.proto = proto; + msg.msg.conn = conn; + TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err); + if (err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + sys_sem_free(&conn->op_completed); + sys_mbox_free(&conn->recvmbox); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + } + return conn; +} + +/** + * Close a netconn 'connection' and free its resources. + * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate + * after this returns. + * + * @param conn the netconn to delete + * @return ERR_OK if the connection was deleted + */ +err_t +netconn_delete(struct netconn *conn) +{ + struct api_msg msg; + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + msg.function = lwip_netconn_do_delconn; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + + netconn_free(conn); + + /* don't care for return value of lwip_netconn_do_delconn since it only calls void functions */ + + return ERR_OK; +} + +/** + * Get the local or remote IP address and port of a netconn. + * For RAW netconns, this returns the protocol instead of a port! + * + * @param conn the netconn to query + * @param addr a pointer to which to save the IP address + * @param port a pointer to which to save the port (or protocol for RAW) + * @param local 1 to get the local IP address, 0 to get the remote one + * @return ERR_CONN for invalid connections + * ERR_OK if the information was retrieved + */ +err_t +netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.ad.ipaddr = ip_2_ipX(addr); + msg.msg.msg.ad.port = port; + msg.msg.msg.ad.local = local; + TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Bind a netconn to a specific local IP address and port. + * Binding one netconn twice might not always be checked correctly! + * + * @param conn the netconn to bind + * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY + * to bind to all addresses) + * @param port the local port to bind the netconn to (not used for RAW) + * @return ERR_OK if bound, any other err_t on failure + */ +err_t +netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; + TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Connect a netconn to a specific remote IP address and port. + * + * @param conn the netconn to connect + * @param addr the remote IP address to connect to + * @param port the remote port to connect to (no used for RAW) + * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise + */ +err_t +netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.bc.ipaddr = addr; + msg.msg.msg.bc.port = port; +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + /* The TCP version waits for the connect to succeed, + so always needs to use message passing. */ + msg.function = lwip_netconn_do_connect; + err = tcpip_apimsg(&msg); + } +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP + else +#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + { + /* UDP and RAW only set flags, so we can use core-locking. */ + TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Disconnect a netconn from its current peer (only valid for UDP netconns). + * + * @param conn the netconn to disconnect + * @return TODO: return value is not set here... + */ +err_t +netconn_disconnect(struct netconn *conn) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Set a TCP netconn into listen mode + * + * @param conn the tcp netconn to set to listen mode + * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1 + * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns + * don't return any error (yet?)) + */ +err_t +netconn_listen_with_backlog(struct netconn *conn, u8_t backlog) +{ +#if LWIP_TCP + struct api_msg msg; + err_t err; + + /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ + LWIP_UNUSED_ARG(backlog); + + LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; +#if TCP_LISTEN_BACKLOG + msg.msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(backlog); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @param new_conn pointer where the new connection is stored + * @return ERR_OK if a new connection has been received or an error + * code otherwise + */ +err_t +netconn_accept(struct netconn *conn, struct netconn **new_conn) +{ +#if LWIP_TCP + struct netconn *newconn; + err_t err; +#if TCP_LISTEN_BACKLOG + struct api_msg msg; +#endif /* TCP_LISTEN_BACKLOG */ + + LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;); + *new_conn = NULL; + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on acceptmbox forever! */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + + if (newconn == NULL) { + /* connection has been aborted */ + NETCONN_SET_SAFE_ERR(conn, ERR_ABRT); + return ERR_ABRT; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + msg.msg.conn = conn; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); +#endif /* TCP_LISTEN_BACKLOG */ + + *new_conn = newconn; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(new_conn); + return ERR_ARG; +#endif /* LWIP_TCP */ +} + +/** + * Receive data: actual implementation that doesn't care whether pbuf or netbuf + * is received + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf/netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +static err_t +netconn_recv_data(struct netconn *conn, void **new_buf) +{ + void *buf = NULL; + u16_t len; + err_t err; +#if LWIP_TCP + struct api_msg msg; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + + err = conn->last_err; + if (ERR_IS_FATAL(err)) { + /* don't recv on fatal errors: this might block the application task + waiting on recvmbox forever! */ + /* @todo: this does not allow us to fetch data that has been put into recvmbox + before the fatal error occurred - is that a problem? */ + return err; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT); + return ERR_TIMEOUT; + } +#else + sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + if (!netconn_get_noautorecved(conn) || (buf == NULL)) { + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); + } + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (buf == NULL) { + API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0); + /* Avoid to lose any previous error code */ + NETCONN_SET_SAFE_ERR(conn, ERR_CLSD); + return ERR_CLSD; + } + len = ((struct pbuf *)buf)->tot_len; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ +#if (LWIP_UDP || LWIP_RAW) + { + LWIP_ASSERT("buf != NULL", buf != NULL); + len = netbuf_len((struct netbuf *)buf); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + +#if LWIP_SO_RCVBUF + SYS_ARCH_DEC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len)); + + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; +} + +/** + * Receive data (in form of a pbuf) from a TCP netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new pbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + * ERR_ARG if conn is not a TCP netconn + */ +err_t +netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf) +{ + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) && + NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;); + + return netconn_recv_data(conn, (void **)new_buf); +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @param new_buf pointer where a new netbuf is stored when received data + * @return ERR_OK if data has been received, an error code otherwise (timeout, + * memory error or another error) + */ +err_t +netconn_recv(struct netconn *conn, struct netbuf **new_buf) +{ +#if LWIP_TCP + struct netbuf *buf = NULL; + err_t err; +#endif /* LWIP_TCP */ + + LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;); + *new_buf = NULL; + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;); + +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + struct pbuf *p = NULL; + /* This is not a listening netconn, since recvmbox is set */ + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + NETCONN_SET_SAFE_ERR(conn, ERR_MEM); + return ERR_MEM; + } + + err = netconn_recv_data(conn, (void **)&p); + if (err != ERR_OK) { + memp_free(MEMP_NETBUF, buf); + return err; + } + LWIP_ASSERT("p != NULL", p != NULL); + + buf->p = p; + buf->ptr = p; + buf->port = 0; + ipX_addr_set_any(LWIP_IPV6, &buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ + { +#if (LWIP_UDP || LWIP_RAW) + return netconn_recv_data(conn, (void **)new_buf); +#endif /* (LWIP_UDP || LWIP_RAW) */ + } +} + +/** + * TCP: update the receive window: by calling this, the application + * tells the stack that it has processed data and is able to accept + * new data. + * ATTENTION: use with care, this is mainly used for sockets! + * Can only be used when calling netconn_set_noautorecved(conn, 1) before. + * + * @param conn the netconn for which to update the receive window + * @param length amount of data processed (ATTENTION: this must be accurate!) + */ +void +netconn_recved(struct netconn *conn, u32_t length) +{ +#if LWIP_TCP + if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && + (netconn_get_noautorecved(conn))) { + struct api_msg msg; + /* Let the stack know that we have taken the data. */ + /* TODO: Speedup: Don't block and wait for the answer here + (to prevent multiple thread-switches). */ + msg.msg.conn = conn; + msg.msg.msg.r.len = length; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv); + } +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(conn); + LWIP_UNUSED_ARG(length); +#endif /* LWIP_TCP */ +} + +/** + * Send data (in form of a netbuf) to a specific remote IP address and port. + * Only to be used for UDP and RAW netconns (not TCP). + * + * @param conn the netconn over which to send data + * @param buf a netbuf containing the data to send + * @param addr the remote IP address to which to send the data + * @param port the remote port to which to send the data + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port) +{ + if (buf != NULL) { + ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &buf->addr, addr); + buf->port = port; + return netconn_send(conn, buf); + } + return ERR_VAL; +} + +/** + * Send data over a UDP or RAW netconn (that is already connected). + * + * @param conn the UDP or RAW netconn over which to send data + * @param buf a netbuf containing the data to send + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_send(struct netconn *conn, struct netbuf *buf) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len)); + msg.msg.conn = conn; + msg.msg.msg.b = buf; + TCPIP_APIMSG(&msg, lwip_netconn_do_send, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Send data over a TCP netconn. + * + * @param conn the TCP netconn over which to send data + * @param dataptr pointer to the application buffer that contains the data to send + * @param size size of the application data to send + * @param apiflags combination of following flags : + * - NETCONN_COPY: data will be copied into memory belonging to the stack + * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent + * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once + * @param bytes_written pointer to a location that receives the number of written bytes + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) +{ + struct api_msg msg; + err_t err; + u8_t dontblock; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + if (dontblock && !bytes_written) { + /* This implies netconn_write() cannot be used for non-blocking send, since + it has no way to return the number of bytes written. */ + return ERR_VAL; + } + + /* non-blocking write sends as much */ + msg.msg.conn = conn; + msg.msg.msg.w.dataptr = dataptr; + msg.msg.msg.w.apiflags = apiflags; + msg.msg.msg.w.len = size; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + msg.msg.msg.w.time_started = sys_now(); + } else { + msg.msg.msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + + /* For locking the core: this _can_ be delayed on low memory/low send buffer, + but if it is, this is done inside api_msg.c:do_write(), so we can use the + non-blocking version here. */ + TCPIP_APIMSG(&msg, lwip_netconn_do_write, err); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock +#if LWIP_SO_SNDTIMEO + || (conn->send_timeout != 0) +#endif /* LWIP_SO_SNDTIMEO */ + ) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = msg.msg.msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close ot shutdown a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = lwip_netconn_do_close; + msg.msg.conn = conn; + /* shutting down both ends is the same as closing */ + msg.msg.msg.sd.shut = how; + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close, + don't use TCPIP_APIMSG here */ + err = tcpip_apimsg(&msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * + * @param conn the UDP netconn for which to change multicast addresses + * @param multiaddr IP address of the multicast group to join or leave + * @param netif_addr the IP address of the network interface on which to send + * the igmp message + * @param join_or_leave flag whether to send a join- or leave-message + * @return ERR_OK if the action was taken, any err_t on error + */ +err_t +netconn_join_leave_group(struct netconn *conn, + ip_addr_t *multiaddr, + ip_addr_t *netif_addr, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.msg.conn = conn; + msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr); + msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr); + msg.msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * Execute a DNS query, only one IP address is returned + * + * @param name a string representation of the DNS host name to query + * @param addr a preallocated ip_addr_t where to store the resolved IP address + * @return ERR_OK: resolving succeeded + * ERR_MEM: memory error, try again later + * ERR_ARG: dns client not initialized or invalid hostname + * ERR_VAL: dns server response was invalid + */ +err_t +netconn_gethostbyname(const char *name, ip_addr_t *addr) +{ + struct dns_api_msg msg; + err_t err; + sys_sem_t sem; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); + + err = sys_sem_new(&sem, 0); + if (err != ERR_OK) { + return err; + } + + msg.name = name; + msg.addr = addr; + msg.err = &err; + msg.sem = &sem; + + tcpip_callback(lwip_netconn_do_gethostbyname, &msg); + sys_sem_wait(&sem); + sys_sem_free(&sem); + + return err; +} +#endif /* LWIP_DNS*/ + +#endif /* LWIP_NETCONN */ diff --git a/external/badvpn_dns/lwip/src/api/api_msg.c b/external/badvpn_dns/lwip/src/api/api_msg.c new file mode 100644 index 00000000..b1a9b772 --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/api_msg.c @@ -0,0 +1,1610 @@ +/** + * @file + * Sequential API Internal module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" + +#include "lwip/ip.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" + +#include "lwip/memp.h" +#include "lwip/tcpip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/mld6.h" + +#include + +#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0) +#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0) + +/* forward declarations */ +#if LWIP_TCP +static err_t lwip_netconn_do_writemore(struct netconn *conn); +static void lwip_netconn_do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only references it and sends it to + * conn->recvmbox + * + * @see raw.h (struct raw_pcb.recv) for parameters and return value + */ +static u8_t +recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; + + LWIP_UNUSED_ARG(addr); + conn = (struct netconn *)arg; + + if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { +#if LWIP_SO_RCVBUF + int recv_avail; + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { + return 0; + } +#endif /* LWIP_SO_RCVBUF */ + /* copy the whole packet into new pbufs */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(q != NULL) { + if (pbuf_copy(q, p) != ERR_OK) { + pbuf_free(q); + q = NULL; + } + } + + if (q != NULL) { + u16_t len; + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr()); + buf->port = pcb->protocol; + + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + } + } + + return 0; /* do not eat the packet */ +} +#endif /* LWIP_RAW*/ + +#if LWIP_UDP +/** + * Receive callback function for UDP netconns. + * Posts the packet to conn->recvmbox or deletes it on memory error. + * + * @see udp.h (struct udp_pcb.recv) for parameters + */ +static void +recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; + u16_t len; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(pcb); /* only used for asserts... */ + LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_udp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb); + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = ipX_next_header_ptr(); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + len = p->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } +} +#endif /* LWIP_UDP */ + +#if LWIP_TCP +/** + * Receive callback function for TCP netconns. + * Posts the packet to conn->recvmbox, but doesn't delete it on errors. + * + * @see tcp.h (struct tcp_pcb.recv) for parameters and return value + */ +static err_t +recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + struct netconn *conn; + u16_t len; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL); + LWIP_ASSERT("recv_tcp must have an argument", arg != NULL); + conn = (struct netconn *)arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if (conn == NULL) { + return ERR_VAL; + } + if (!sys_mbox_valid(&conn->recvmbox)) { + /* recvmbox already deleted */ + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } + return ERR_OK; + } + /* Unlike for UDP or RAW pcbs, don't check for available space + using recv_avail since that could break the connection + (data is already ACKed) */ + + /* don't overwrite fatal errors! */ + NETCONN_SET_SAFE_ERR(conn, err); + + if (p != NULL) { + len = p->tot_len; + } else { + len = 0; + } + + if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { + /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */ + return ERR_MEM; + } else { +#if LWIP_SO_RCVBUF + SYS_ARCH_INC(conn->recv_avail, len); +#endif /* LWIP_SO_RCVBUF */ + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); + } + + return ERR_OK; +} + +/** + * Poll callback function for TCP netconns. + * Wakes up an application thread that waits for a connection to close + * or data to be sent. The application thread then takes the + * appropriate action to go on. + * + * Signals the conn->sem. + * netconn_close waits for conn->sem if closing failed. + * + * @see tcp.h (struct tcp_pcb.poll) for parameters and return value + */ +static err_t +poll_tcp(void *arg, struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_do_close_internal(conn); + } + /* @todo: implement connect timeout here? */ + + /* Did a nonblocking write fail before? Then check available write-space. */ + if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + } + + return ERR_OK; +} + +/** + * Sent callback function for TCP netconns. + * Signals the conn->sem and calls API_EVENT. + * netconn_write waits for conn->sem if send buffer is low. + * + * @see tcp.h (struct tcp_pcb.sent) for parameters and return value + */ +static err_t +sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len) +{ + struct netconn *conn = (struct netconn *)arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_do_close_internal(conn); + } + + if (conn) { + /* If the queued byte- or pbuf-count drops below the configured low-water limit, + let select mark this pcb as writable again. */ + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) && + (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) { + conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE; + API_EVENT(conn, NETCONN_EVT_SENDPLUS, len); + } + } + + return ERR_OK; +} + +/** + * Error callback function for TCP netconns. + * Signals conn->sem, posts to all conn mboxes and calls API_EVENT. + * The application thread has then to decide what to do. + * + * @see tcp.h (struct tcp_pcb.err) for parameters + */ +static void +err_tcp(void *arg, err_t err) +{ + struct netconn *conn; + enum netconn_state old_state; + SYS_ARCH_DECL_PROTECT(lev); + + conn = (struct netconn *)arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + /* no check since this is always fatal! */ + SYS_ARCH_PROTECT(lev); + conn->last_err = err; + SYS_ARCH_UNPROTECT(lev); + + /* reset conn->state now before waking up other threads */ + old_state = conn->state; + conn->state = NETCONN_NONE; + + /* Notify the user layer about a connection error. Used to signal + select. */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + /* Try to release selects pending on 'read' or 'write', too. + They will get an error if they actually try to read or write. */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + /* pass NULL-message to recvmbox to wake up pending recv */ + if (sys_mbox_valid(&conn->recvmbox)) { + /* use trypost to prevent deadlock */ + sys_mbox_trypost(&conn->recvmbox, NULL); + } + /* pass NULL-message to acceptmbox to wake up pending accept */ + if (sys_mbox_valid(&conn->acceptmbox)) { + /* use trypost to preven deadlock */ + sys_mbox_trypost(&conn->acceptmbox, NULL); + } + + if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) || + (old_state == NETCONN_CONNECT)) { + /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary + since the pcb has already been deleted! */ + int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + + if (!was_nonblocking_connect) { + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(&conn->op_completed); + } + } else { + LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL); + } +} + +/** + * Setup a tcp_pcb with the correct callback function pointers + * and their arguments. + * + * @param conn the TCP netconn to setup + */ +static void +setup_tcp(struct netconn *conn) +{ + struct tcp_pcb *pcb; + + pcb = conn->pcb.tcp; + tcp_arg(pcb, conn); + tcp_recv(pcb, recv_tcp); + tcp_sent(pcb, sent_tcp); + tcp_poll(pcb, poll_tcp, 4); + tcp_err(pcb, err_tcp); +} + +/** + * Accept callback function for TCP netconns. + * Allocates a new netconn and posts that to conn->acceptmbox. + * + * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value + */ +static err_t +accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + struct netconn *newconn; + struct netconn *conn = (struct netconn *)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state))); + + if (!sys_mbox_valid(&conn->acceptmbox)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n")); + return ERR_VAL; + } + + /* We have to set the callback here even though + * the new socket is unknown. conn->socket is marked as -1. */ + newconn = netconn_alloc(conn->type, conn->callback); + if (newconn == NULL) { + return ERR_MEM; + } + newconn->pcb.tcp = newpcb; + setup_tcp(newconn); + /* no protection: when creating the pcb, the netconn is not yet known + to the application thread */ + newconn->last_err = err; + + if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the pcb is aborted in tcp_process(), + so do nothing here! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 4); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ + newconn->pcb.tcp = NULL; + /* no need to drain since we know the recvmbox is empty. */ + sys_mbox_free(&newconn->recvmbox); + sys_mbox_set_invalid(&newconn->recvmbox); + netconn_free(newconn); + return ERR_MEM; + } else { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Create a new pcb of a specific type. + * Called from lwip_netconn_do_newconn(). + * + * @param msg the api_msg_msg describing the connection type + * @return msg->conn->err, but the return value is currently ignored + */ +static void +pcb_new(struct api_msg_msg *msg) +{ + LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL); + + /* Allocate a PCB for this connection */ + switch(NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.n.proto); + if(msg->conn->pcb.raw != NULL) { + raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn); + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(msg->conn->pcb.udp != NULL) { +#if LWIP_UDPLITE + if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM); + } + udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn); + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(msg->conn->pcb.tcp != NULL) { + setup_tcp(msg->conn); + } + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + return; + } + if (msg->conn->pcb.ip == NULL) { + msg->err = ERR_MEM; + } +#if LWIP_IPV6 + else { + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + ip_set_v6(msg->conn->pcb.ip, 1); + } + } +#endif /* LWIP_IPV6 */ +} + +/** + * Create a new pcb of a specific type inside a netconn. + * Called from netconn_new_with_proto_and_callback. + * + * @param msg the api_msg_msg describing the connection type + */ +void +lwip_netconn_do_newconn(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if(msg->conn->pcb.tcp == NULL) { + pcb_new(msg); + } + /* Else? This "new" connection already has a PCB allocated. */ + /* Is this an error condition? Should it be deleted? */ + /* We currently just are happy and return. */ + + TCPIP_APIMSG_ACK(msg); +} + +/** + * Create a new netconn (of a specific type) that has a callback function. + * The corresponding pcb is NOT created! + * + * @param t the type of 'connection' to create (@see enum netconn_type) + * @param proto the IP protocol for RAW IP pcbs + * @param callback a function to call on status changes (RX available, TX'ed) + * @return a newly allocated struct netconn or + * NULL on memory error + */ +struct netconn* +netconn_alloc(enum netconn_type t, netconn_callback callback) +{ + struct netconn *conn; + int size; + + conn = (struct netconn *)memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->last_err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + + /* If all sizes are the same, every compiler should optimize this switch to nothing, */ + switch(NETCONNTYPE_GROUP(t)) { +#if LWIP_RAW + case NETCONN_RAW: + size = DEFAULT_RAW_RECVMBOX_SIZE; + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + size = DEFAULT_UDP_RECVMBOX_SIZE; + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + size = DEFAULT_TCP_RECVMBOX_SIZE; + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0); + goto free_and_return; + } + + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + goto free_and_return; + } + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + sys_sem_free(&conn->op_completed); + goto free_and_return; + } + +#if LWIP_TCP + sys_mbox_set_invalid(&conn->acceptmbox); +#endif + conn->state = NETCONN_NONE; +#if LWIP_SOCKET + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; +#endif /* LWIP_SOCKET */ + conn->callback = callback; +#if LWIP_TCP + conn->current_msg = NULL; + conn->write_offset = 0; +#endif /* LWIP_TCP */ +#if LWIP_SO_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + conn->recv_timeout = 0; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + conn->recv_bufsize = RECV_BUFSIZE_DEFAULT; + conn->recv_avail = 0; +#endif /* LWIP_SO_RCVBUF */ + conn->flags = 0; + return conn; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; +} + +/** + * Delete a netconn and all its resources. + * The pcb is NOT freed (since we might not be in the right thread context do this). + * + * @param conn the netconn to free + */ +void +netconn_free(struct netconn *conn) +{ + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + LWIP_ASSERT("recvmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->recvmbox)); +#if LWIP_TCP + LWIP_ASSERT("acceptmbox must be deallocated before calling this function", + !sys_mbox_valid(&conn->acceptmbox)); +#endif /* LWIP_TCP */ + + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); + + memp_free(MEMP_NETCONN, conn); +} + +/** + * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in + * these mboxes + * + * @param conn the netconn to free + * @bytes_drained bytes drained from recvmbox + * @accepts_drained pending connections drained from acceptmbox + */ +static void +netconn_drain(struct netconn *conn) +{ + void *mem; +#if LWIP_TCP + struct pbuf *p; +#endif /* LWIP_TCP */ + + /* This runs in tcpip_thread, so we don't need to lock against rx packets */ + + /* Delete and drain the recvmbox. */ + if (sys_mbox_valid(&conn->recvmbox)) { + while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { +#if LWIP_TCP + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) { + if(mem != NULL) { + p = (struct pbuf*)mem; + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_recved(conn->pcb.tcp, p->tot_len); + } + pbuf_free(p); + } + } else +#endif /* LWIP_TCP */ + { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(&conn->recvmbox); + sys_mbox_set_invalid(&conn->recvmbox); + } + + /* Delete and drain the acceptmbox. */ +#if LWIP_TCP + if (sys_mbox_valid(&conn->acceptmbox)) { + while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + struct netconn *newconn = (struct netconn *)mem; + /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */ + /* pcb might be set to NULL already by err_tcp() */ + if (conn->pcb.tcp != NULL) { + tcp_accepted(conn->pcb.tcp); + } + /* drain recvmbox */ + netconn_drain(newconn); + if (newconn->pcb.tcp != NULL) { + tcp_abort(newconn->pcb.tcp); + newconn->pcb.tcp = NULL; + } + netconn_free(newconn); + } + sys_mbox_free(&conn->acceptmbox); + sys_mbox_set_invalid(&conn->acceptmbox); + } +#endif /* LWIP_TCP */ +} + +#if LWIP_TCP +/** + * Internal helper function to close a TCP netconn: since this sometimes + * doesn't work at the first attempt, this function is called from multiple + * places. + * + * @param conn the TCP netconn to close + */ +static void +lwip_netconn_do_close_internal(struct netconn *conn) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)); + LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE)); + LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing */ + close = shut == NETCONN_SHUT_RDWR; + + /* Set back some callback pointers */ + if (close) { + tcp_arg(conn->pcb.tcp, NULL); + } + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } + if (shut_tx) { + tcp_sent(conn->pcb.tcp, NULL); + } + if (close) { + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + } + /* Try to close the connection */ + if (close) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx); + } + if (err == ERR_OK) { + /* Closing succeeded */ + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (close) { + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + /* Trigger select() in socket layer. Make sure everybody notices activity + on the connection, error first! */ + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } + /* wake up the application task */ + sys_sem_signal(&conn->op_completed); + } else { + /* Closing failed, restore some of the callbacks */ + /* Closing of listen pcb will never fail! */ + LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN)); + tcp_sent(conn->pcb.tcp, sent_tcp); + tcp_poll(conn->pcb.tcp, poll_tcp, 4); + tcp_err(conn->pcb.tcp, err_tcp); + tcp_arg(conn->pcb.tcp, conn); + /* don't restore recv callback: we don't want to receive any more data */ + } + /* If closing didn't succeed, we get called again either + from poll_tcp or from sent_tcp */ +} +#endif /* LWIP_TCP */ + +/** + * Delete the pcb inside a netconn. + * Called from netconn_delete. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_delconn(struct api_msg_msg *msg) +{ + /* @todo TCP: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && + (msg->conn->state != NETCONN_LISTEN) && + (msg->conn->state != NETCONN_CONNECT)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else { + LWIP_ASSERT("blocking connect in progress", + (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn)); + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + + if (msg->conn->pcb.tcp != NULL) { + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->msg.sd.shut = NETCONN_SHUT_RDWR; + msg->conn->current_msg = msg; + lwip_netconn_do_close_internal(msg->conn); + /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing + the application thread, so we can return at this point! */ + return; +#endif /* LWIP_TCP */ + default: + break; + } + msg->conn->pcb.tcp = NULL; + } + /* tcp netconns don't come here! */ + + /* @todo: this lets select make the socket readable and writable, + which is wrong! errfd instead? */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + } + if (sys_sem_valid(&msg->conn->op_completed)) { + sys_sem_signal(&msg->conn->op_completed); + } +} + +/** + * Bind a pcb contained in a netconn + * Called from netconn_bind. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to bind to + */ +void +lwip_netconn_do_bind(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_VAL; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has + * been established (or reset by the remote host). + * + * @see tcp.h (struct tcp_pcb.connected) for parameters and return values + */ +static err_t +lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + + LWIP_UNUSED_ARG(pcb); + + conn = (struct netconn *)arg; + + if (conn == NULL) { + return ERR_VAL; + } + + LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT); + LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect", + (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn)); + + if (conn->current_msg != NULL) { + conn->current_msg->err = err; + } + if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (!was_blocking) { + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(&conn->op_completed); + } + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Connect a pcb contained inside a netconn + * Called from netconn_connect. + * + * @param msg the api_msg_msg pointing to the connection and containing + * the IP address and port to connect to + */ +void +lwip_netconn_do_connect(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.tcp == NULL) { + /* This may happen when calling netconn_connect() a second time */ + msg->err = ERR_CLSD; + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; + } + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + /* Prevent connect while doing any other action. */ + if (msg->conn->state != NETCONN_NONE) { + msg->err = ERR_ISCONN; + } else { + setup_tcp(msg->conn); + msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, + msg->msg.bc.port, lwip_netconn_do_connected); + if (msg->err == ERR_OK) { + u8_t non_blocking = netconn_is_nonblocking(msg->conn); + msg->conn->state = NETCONN_CONNECT; + SET_NONBLOCKING_CONNECT(msg->conn, non_blocking); + if (non_blocking) { + msg->err = ERR_INPROGRESS; + } else { + msg->conn->current_msg = msg; + /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } + } + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(&msg->conn->op_completed); + return; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), + so use TCPIP_APIMSG_ACK() here. */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * Connect a pcb contained inside a netconn + * Only used for UDP netconns. + * Called from netconn_disconnect. + * + * @param msg the api_msg_msg pointing to the connection to disconnect + */ +void +lwip_netconn_do_disconnect(struct api_msg_msg *msg) +{ +#if LWIP_UDP + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + udp_disconnect(msg->conn->pcb.udp); + msg->err = ERR_OK; + } else +#endif /* LWIP_UDP */ + { + msg->err = ERR_VAL; + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Set a TCP pcb contained in a netconn into listen mode + * Called from netconn_listen. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_listen(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { + struct tcp_pcb* lpcb; +#if LWIP_IPV6 + if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen_dual(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } else +#endif /* LWIP_IPV6 */ + { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } + if (lpcb == NULL) { + /* in this case, the old pcb is still allocated */ + msg->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (sys_mbox_valid(&msg->conn->recvmbox)) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(&msg->conn->recvmbox); + sys_mbox_set_invalid(&msg->conn->recvmbox); + } + msg->err = ERR_OK; + if (!sys_mbox_valid(&msg->conn->acceptmbox)) { + msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE); + } + if (msg->err == ERR_OK) { + msg->conn->state = NETCONN_LISTEN; + msg->conn->pcb.tcp = lpcb; + tcp_arg(msg->conn->pcb.tcp, msg->conn); + tcp_accept(msg->conn->pcb.tcp, accept_function); + } else { + /* since the old pcb is already deallocated, free lpcb now */ + tcp_close(lpcb); + msg->conn->pcb.tcp = NULL; + } + } + } + } else { + msg->err = ERR_ARG; + } + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a RAW or UDP pcb contained in a netconn + * Called from netconn_send + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_send(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr)); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } else { + msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, + ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port, + msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); + } +#else /* LWIP_CHECKSUM_ON_COPY */ + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { + msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * Indicate data has been received from a TCP pcb contained in a netconn + * Called from netconn_recv + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_recv(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { +#if TCP_LISTEN_BACKLOG + if (msg->conn->pcb.tcp->state == LISTEN) { + tcp_accepted(msg->conn->pcb.tcp); + } else +#endif /* TCP_LISTEN_BACKLOG */ + { + u32_t remaining = msg->msg.r.len; + do { + u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining; + tcp_recved(msg->conn->pcb.tcp, recved); + remaining -= recved; + }while(remaining != 0); + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * See if more data needs to be written from a previous call to netconn_write. + * Called initially from lwip_netconn_do_write. If the first call can't send all data + * (because of low memory or empty send-buffer), this function is called again + * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the + * blocking application thread (waiting in netconn_write) is released. + * + * @param conn netconn (that is currently in state NETCONN_WRITE) to process + * @return ERR_OK + * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished + */ +static err_t +lwip_netconn_do_writemore(struct netconn *conn) +{ + err_t err; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock = netconn_is_nonblocking(conn) || + (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); + u8_t apiflags = conn->current_msg->msg.w.apiflags; + + LWIP_ASSERT("conn != NULL", conn != NULL); + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", + conn->write_offset < conn->current_msg->msg.w.len); + +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset; + diff = conn->current_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } else { + len = (u16_t)diff; + } + available = tcp_sndbuf(conn->pcb.tcp); + if (available < len) { + /* don't try to write more than sendbuf */ + len = available; + if (dontblock){ + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + /* if OK or memory error, check available space */ + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) || + (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) { + /* The queued byte- or pbuf-count exceeds the configured low-water limit, + let select mark this pcb as non-writable. */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } + + if (err == ERR_OK) { + conn->write_offset += len; + if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if ((err == ERR_MEM) && !dontblock) { + /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called + we do NOT return to the application thread, since ERR_MEM is + only a temporary error! */ + + /* tcp_write returned ERR_MEM, try tcp_output anyway */ + tcp_output(conn->pcb.tcp); + +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + write_finished = 1; + conn->current_msg->msg.w.len = 0; + } + } + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->current_msg->err = err; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0) +#endif + { + sys_sem_signal(&conn->op_completed); + } + } +#if LWIP_TCPIP_CORE_LOCKING + else + return ERR_MEM; +#endif + return ERR_OK; +} +#endif /* LWIP_TCP */ + +/** + * Send some data on a TCP pcb contained in a netconn + * Called from netconn_write + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_write(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { +#if LWIP_TCP + if (msg->conn->state != NETCONN_NONE) { + /* netconn is connecting, closing or in blocking write */ + msg->err = ERR_INPROGRESS; + } else if (msg->conn->pcb.tcp != NULL) { + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by lwip_netconn_do_writemore */ + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0); + msg->conn->current_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED; + if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(&msg->conn->op_completed, 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG + since lwip_netconn_do_writemore ACKs it! */ + return; + } else { + msg->err = ERR_CONN; + } +#else /* LWIP_TCP */ + msg->err = ERR_VAL; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Return a connection's local or remote address + * Called from netconn_getaddr + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + if (msg->msg.ad.local) { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->local_ip); + } else { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->remote_ip); + } + msg->err = ERR_OK; + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; + } else { + /* return an error as connecting is only a helper for upper layers */ + msg->err = ERR_CONN; + } + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.ad.local) { + *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; + } else { + if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { + msg->err = ERR_CONN; + } else { + *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port); + break; +#endif /* LWIP_TCP */ + default: + LWIP_ASSERT("invalid netconn_type", 0); + break; + } + } else { + msg->err = ERR_CONN; + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * Close a TCP pcb contained in a netconn + * Called from netconn_close + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_close(struct api_msg_msg *msg) +{ +#if LWIP_TCP + /* @todo: abort running write/connect? */ + if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) { + /* this only happens for TCP netconns */ + LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) { + if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { + /* LISTEN doesn't support half shutdown */ + msg->err = ERR_CONN; + } else { + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + lwip_netconn_do_close_internal(msg->conn); + /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_VAL; + } + sys_sem_signal(&msg->conn->op_completed); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_do_join_leave_group(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { +#if LWIP_UDP +#if LWIP_IPV6 && LWIP_IPV6_MLD + if (PCB_ISIPV6(msg->conn->pcb.udp)) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } else { + msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr), + ipX_2_ip6(msg->msg.jl.multiaddr)); + } + } + else +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + { +#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } else { + msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr), + ipX_2_ip(msg->msg.jl.multiaddr)); + } +#endif /* LWIP_IGMP */ + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } else { + msg->err = ERR_CONN; + } + } + TCPIP_APIMSG_ACK(msg); +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +/** + * Callback function that is called when DNS name is resolved + * (or on timeout). A waiting application thread is waked up by + * signaling the semaphore. + */ +static void +lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0); + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + *msg->err = ERR_VAL; + } else { + /* address was resolved */ + *msg->err = ERR_OK; + *msg->addr = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +lwip_netconn_do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_do_dns_found, msg); + if (*msg->err != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(msg->sem); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/external/badvpn_dns/lwip/src/api/err.c b/external/badvpn_dns/lwip/src/api/err.c new file mode 100644 index 00000000..92fa8b7d --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/err.c @@ -0,0 +1,75 @@ +/** + * @file + * Error Management module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/err.h" + +#ifdef LWIP_DEBUG + +static const char *err_strerr[] = { + "Ok.", /* ERR_OK 0 */ + "Out of memory error.", /* ERR_MEM -1 */ + "Buffer error.", /* ERR_BUF -2 */ + "Timeout.", /* ERR_TIMEOUT -3 */ + "Routing problem.", /* ERR_RTE -4 */ + "Operation in progress.", /* ERR_INPROGRESS -5 */ + "Illegal value.", /* ERR_VAL -6 */ + "Operation would block.", /* ERR_WOULDBLOCK -7 */ + "Address in use.", /* ERR_USE -8 */ + "Already connected.", /* ERR_ISCONN -9 */ + "Connection aborted.", /* ERR_ABRT -10 */ + "Connection reset.", /* ERR_RST -11 */ + "Connection closed.", /* ERR_CLSD -12 */ + "Not connected.", /* ERR_CONN -13 */ + "Illegal argument.", /* ERR_ARG -14 */ + "Low-level netif error.", /* ERR_IF -15 */ +}; + +/** + * Convert an lwip internal error to a string representation. + * + * @param err an lwip internal err_t + * @return a string representation for err + */ +const char * +lwip_strerr(err_t err) +{ + return err_strerr[-err]; + +} + +#endif /* LWIP_DEBUG */ diff --git a/external/badvpn_dns/lwip/src/api/netbuf.c b/external/badvpn_dns/lwip/src/api/netbuf.c new file mode 100644 index 00000000..0ccd2bce --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/netbuf.c @@ -0,0 +1,245 @@ +/** + * @file + * Network buffer management + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netbuf.h" +#include "lwip/memp.h" + +#include + +/** + * Create (allocate) and initialize a new netbuf. + * The netbuf doesn't yet contain a packet buffer! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + ipX_addr_set_any(LWIP_IPV6, &buf->addr); + buf->port = 0; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + buf->flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + buf->toport_chksum = 0; +#if LWIP_NETBUF_RECVINFO + ipX_addr_set_any(LWIP_IPV6, &buf->toaddr); +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * + * @param buf pointer to a netbuf allocated by netbuf_new() + */ +void +netbuf_delete(struct netbuf *buf) +{ + if (buf != NULL) { + if (buf->p != NULL) { + pbuf_free(buf->p); + buf->p = buf->ptr = NULL; + } + memp_free(MEMP_NETBUF, buf); + } +} + +/** + * Allocate memory for a packet buffer for a given netbuf. + * + * @param buf the netbuf for which to allocate a packet buffer + * @param size the size of the packet buffer to allocate + * @return pointer to the allocated memory + * NULL if no memory could be allocated + */ +void * +netbuf_alloc(struct netbuf *buf, u16_t size) +{ + LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;); + + /* Deallocate any previously allocated memory. */ + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (buf->p == NULL) { + return NULL; + } + LWIP_ASSERT("check that first pbuf can hold size", + (buf->p->len >= size)); + buf->ptr = buf->p; + return buf->p->payload; +} + +/** + * Free the packet buffer included in a netbuf + * + * @param buf pointer to the netbuf which contains the packet buffer to free + */ +void +netbuf_free(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = buf->ptr = NULL; +} + +/** + * Let a netbuf reference existing (non-volatile) data. + * + * @param buf netbuf which should reference the data + * @param dataptr pointer to the data to reference + * @param size size of the data + * @return ERR_OK if data is referenced + * ERR_MEM if data couldn't be referenced due to lack of memory + */ +err_t +netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size) +{ + LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;); + if (buf->p != NULL) { + pbuf_free(buf->p); + } + buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (buf->p == NULL) { + buf->ptr = NULL; + return ERR_MEM; + } + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; + return ERR_OK; +} + +/** + * Chain one netbuf to another (@see pbuf_chain) + * + * @param head the first netbuf + * @param tail netbuf to chain after head, freed by this function, may not be reference after returning + */ +void +netbuf_chain(struct netbuf *head, struct netbuf *tail) +{ + LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;); + LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;); + pbuf_cat(head->p, tail->p); + head->ptr = head->p; + memp_free(MEMP_NETBUF, tail); +} + +/** + * Get the data pointer and length of the data inside a netbuf. + * + * @param buf netbuf to get the data from + * @param dataptr pointer to a void pointer where to store the data pointer + * @param len pointer to an u16_t where the length of the data is stored + * @return ERR_OK if the information was retreived, + * ERR_BUF on error. + */ +err_t +netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len) +{ + LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;); + LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;); + + if (buf->ptr == NULL) { + return ERR_BUF; + } + *dataptr = buf->ptr->payload; + *len = buf->ptr->len; + return ERR_OK; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the next part. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + * @return -1 if there is no next part + * 1 if moved to the next part but now there is no next part + * 0 if moved to the next part and there are still more parts + */ +s8_t +netbuf_next(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;); + if (buf->ptr->next == NULL) { + return -1; + } + buf->ptr = buf->ptr->next; + if (buf->ptr->next == NULL) { + return 1; + } + return 0; +} + +/** + * Move the current data pointer of a packet buffer contained in a netbuf + * to the beginning of the packet. + * The packet buffer itself is not modified. + * + * @param buf the netbuf to modify + */ +void +netbuf_first(struct netbuf *buf) +{ + LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;); + buf->ptr = buf->p; +} + +#endif /* LWIP_NETCONN */ diff --git a/external/badvpn_dns/lwip/src/api/netdb.c b/external/badvpn_dns/lwip/src/api/netdb.c new file mode 100644 index 00000000..6a4bac56 --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/netdb.c @@ -0,0 +1,353 @@ +/** + * @file + * API functions for name resolving + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/netdb.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include "lwip/err.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/api.h" +#include "lwip/dns.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + ip_addr_t *addr_list[2]; + ip_addr_t addr; + char *aliases; +}; + +/** h_errno is exported in netdb.h for access by applications. */ +#if LWIP_DNS_API_DECLARE_H_ERRNO +int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */ + +/** define "hostent" variables storage: 0 if we use a static (but unprotected) + * set of variables for lwip_gethostbyname, 1 if we use a local storage */ +#ifndef LWIP_DNS_API_HOSTENT_STORAGE +#define LWIP_DNS_API_HOSTENT_STORAGE 0 +#endif + +/** define "hostent" variables storage */ +#if LWIP_DNS_API_HOSTENT_STORAGE +#define HOSTENT_STORAGE +#else +#define HOSTENT_STORAGE static +#endif /* LWIP_DNS_API_STATIC_HOSTENT */ + +/** + * Returns an entry containing addresses of address family AF_INET + * for the host with name name. + * Due to dns_gethostbyname limitations, only one address is returned. + * + * @param name the hostname to resolve + * @return an entry containing addresses of address family AF_INET + * for the host with name name + */ +struct hostent* +lwip_gethostbyname(const char *name) +{ + err_t err; + ip_addr_t addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE ip_addr_t s_hostent_addr; + HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2]; + + /* query host IP address */ + err = netconn_gethostbyname(name, &addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + h_errno = HOST_NOT_FOUND; + return NULL; + } + + /* fill hostent */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = NULL; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char**)&s_phostent_addr; + +#if DNS_DEBUG + /* dump hostent */ + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases)); + if (s_hostent.h_aliases != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_aliases[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx])); + } + } + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length)); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list)); + if (s_hostent.h_addr_list != NULL) { + u8_t idx; + for ( idx=0; s_hostent.h_addr_list[idx]; idx++) { + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx])); + LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx]))); + } + } +#endif /* DNS_DEBUG */ + +#if LWIP_DNS_API_HOSTENT_STORAGE + /* this function should return the "per-thread" hostent after copy from s_hostent */ + return sys_thread_hostent(&s_hostent); +#else + return &s_hostent; +#endif /* LWIP_DNS_API_HOSTENT_STORAGE */ +} + +/** + * Thread-safe variant of lwip_gethostbyname: instead of using a static + * buffer, this function takes buffer and errno pointers as arguments + * and uses these for the result. + * + * @param name the hostname to resolve + * @param ret pre-allocated struct where to store the result + * @param buf pre-allocated buffer where to store additional data + * @param buflen the size of buf + * @param result pointer to a hostent pointer that is set to ret on success + * and set to zero on error + * @param h_errnop pointer to an int where to store errors (instead of modifying + * the global h_errno) + * @return 0 on success, non-zero on error, additional error information + * is stored in *h_errnop instead of h_errno to be thread-safe + */ +int +lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop) +{ + err_t err; + struct gethostbyname_r_helper *h; + char *hostname; + size_t namelen; + int lh_errno; + + if (h_errnop == NULL) { + /* ensure h_errnop is never NULL */ + h_errnop = &lh_errno; + } + + if (result == NULL) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + /* first thing to do: set *result to nothing */ + *result = NULL; + if ((name == NULL) || (ret == NULL) || (buf == NULL)) { + /* not all arguments given */ + *h_errnop = EINVAL; + return -1; + } + + namelen = strlen(name); + if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) { + /* buf can't hold the data needed + a copy of name */ + *h_errnop = ERANGE; + return -1; + } + + h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf); + hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper); + + /* query host IP address */ + err = netconn_gethostbyname(name, &h->addr); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err)); + *h_errnop = HOST_NOT_FOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; + h->aliases = NULL; + ret->h_name = hostname; + ret->h_aliases = &h->aliases; + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&h->addr_list; + + /* set result != NULL */ + *result = ret; + + /* return success */ + return 0; +} + +/** + * Frees one or more addrinfo structures returned by getaddrinfo(), along with + * any additional storage associated with those structures. If the ai_next field + * of the structure is not null, the entire list of structures is freed. + * + * @param ai struct addrinfo to free + */ +void +lwip_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + while (ai != NULL) { + next = ai->ai_next; + memp_free(MEMP_NETDB, ai); + ai = next; + } +} + +/** + * Translates the name of a service location (for example, a host name) and/or + * a service name and returns a set of socket addresses and associated + * information to be used in creating a socket with which to address the + * specified service. + * Memory for the result is allocated internally and must be freed by calling + * lwip_freeaddrinfo()! + * + * Due to a limitation in dns_gethostbyname, only the first address of a + * host is returned. + * Also, service names are not supported (only port numbers)! + * + * @param nodename descriptive name or address string of the host + * (may be NULL -> local address) + * @param servname port number as string of NULL + * @param hints structure containing input values that set socktype and protocol + * @param res pointer to a pointer where to store the result (set to NULL on failure) + * @return 0 on success, non-zero on failure + */ +int +lwip_getaddrinfo(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + err_t err; + ip_addr_t addr; + struct addrinfo *ai; + struct sockaddr_in *sa = NULL; + int port_nr = 0; + size_t total_size; + size_t namelen = 0; + + if (res == NULL) { + return EAI_FAIL; + } + *res = NULL; + if ((nodename == NULL) && (servname == NULL)) { + return EAI_NONAME; + } + + if (servname != NULL) { + /* service name specified: convert to port number + * @todo?: currently, only ASCII integers (port numbers) are supported! */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) { + return EAI_SERVICE; + } + } + + if (nodename != NULL) { + /* service location specified, try to resolve */ + err = netconn_gethostbyname(nodename, &addr); + if (err != ERR_OK) { + return EAI_FAIL; + } + } else { + /* service location specified, use loopback address */ + ip_addr_set_loopback(&addr); + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in); + if (nodename != NULL) { + namelen = strlen(nodename); + LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!", + total_size <= NETDB_ELEM_SIZE); + ai = (struct addrinfo *)memp_malloc(MEMP_NETDB); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + inet_addr_from_ipaddr(&sa->sin_addr, &addr); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons((u16_t)port_nr); + + /* set up addrinfo */ + ai->ai_family = AF_INET; + if (hints != NULL) { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != NULL) { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in)); + MEMCPY(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_in); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + + return 0; +memerr: + if (ai != NULL) { + memp_free(MEMP_NETDB, ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/external/badvpn_dns/lwip/src/api/netifapi.c b/external/badvpn_dns/lwip/src/api/netifapi.c new file mode 100644 index 00000000..81403f82 --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/netifapi.c @@ -0,0 +1,160 @@ +/** + * @file + * Network Interface Sequential API module + * + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netifapi.h" +#include "lwip/tcpip.h" + +/** + * Call netif_add() inside the tcpip_thread context. + */ +static void +netifapi_do_netif_add(struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw, + msg->msg.add.state, + msg->msg.add.init, + msg->msg.add.input)) { + msg->err = ERR_IF; + } else { + msg->err = ERR_OK; + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_set_addr() inside the tcpip_thread context. + */ +static void +netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + msg->msg.add.ipaddr, + msg->msg.add.netmask, + msg->msg.add.gw); + msg->err = ERR_OK; + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the + * tcpip_thread context. + */ +static void +netifapi_do_netif_common(struct netifapi_msg_msg *msg) +{ + if (msg->msg.common.errtfunc != NULL) { + msg->err = msg->msg.common.errtfunc(msg->netif); + } else { + msg->err = ERR_OK; + msg->msg.common.voidfunc(msg->netif); + } + TCPIP_NETIFAPI_ACK(msg); +} + +/** + * Call netif_add() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_add() + */ +err_t +netifapi_netif_add(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_add; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + msg.msg.msg.add.state = state; + msg.msg.msg.add.init = init; + msg.msg.msg.add.input = input; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * Call netif_set_addr() in a thread-safe way by running that function inside the + * tcpip_thread context. + * + * @note for params @see netif_set_addr() + */ +err_t +netifapi_netif_set_addr(struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_set_addr; + msg.msg.netif = netif; + msg.msg.msg.add.ipaddr = ipaddr; + msg.msg.msg.add.netmask = netmask; + msg.msg.msg.add.gw = gw; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +/** + * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe + * way by running that function inside the tcpip_thread context. + * + * @note use only for functions where there is only "netif" parameter. + */ +err_t +netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc) +{ + struct netifapi_msg msg; + msg.function = netifapi_do_netif_common; + msg.msg.netif = netif; + msg.msg.msg.common.voidfunc = voidfunc; + msg.msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&msg); + return msg.msg.err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/external/badvpn_dns/lwip/src/api/sockets.c b/external/badvpn_dns/lwip/src/api/sockets.c new file mode 100644 index 00000000..66036712 --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/sockets.c @@ -0,0 +1,2555 @@ +/** + * @file + * Sockets BSD-Like API module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + * Improved by Marc Boucher and David Haas + * + */ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sockets.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcpip.h" +#include "lwip/pbuf.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \ + (sin)->sin_len = sizeof(struct sockaddr_in); \ + (sin)->sin_family = AF_INET; \ + (sin)->sin_port = htons((port)); \ + inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \ + memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) +#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \ + inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \ + (port) = ntohs((sin)->sin_port); }while(0) + +#if LWIP_IPV6 +#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ + ((namelen) == sizeof(struct sockaddr_in6))) +#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ + ((name)->sa_family == AF_INET6)) +#define SOCK_ADDR_TYPE_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \ + (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ + (sin6)->sin6_family = AF_INET6; \ + (sin6)->sin6_port = htons((port)); \ + (sin6)->sin6_flowinfo = 0; \ + inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0) +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \ + inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \ + (port) = ntohs((sin6)->sin6_port); }while(0) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ + (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) +#else /* LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#endif /* LWIP_IPV6 */ + +#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ + IS_SOCK_ADDR_TYPE_VALID(name)) +#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ + SOCK_ADDR_TYPE_MATCH(name, sock)) +#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) + + + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_sock { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + void *lastdata; + /** offset in the data that was left from the previous read */ + u16_t lastoffset; + /** number of times data was received, set by event_callback(), + tested by the receive and select functions */ + s16_t rcvevent; + /** number of times data was ACKed (free send buffer), set by event_callback(), + tested by select */ + u16_t sendevent; + /** error happened for this socket, set by event_callback(), tested by select */ + u16_t errevent; + /** last error that occurred on this socket */ + int err; + /** counter of how many threads are waiting for this socket using select */ + int select_waiting; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** Pointer to the previous waiting task */ + struct lwip_select_cb *prev; + /** readset passed to select */ + fd_set *readset; + /** writeset passed to select */ + fd_set *writeset; + /** unimplemented: exceptset passed to select */ + fd_set *exceptset; + /** don't signal the same semaphore twice: set to 1 when signalled */ + int sem_signalled; + /** semaphore to wake up a task waiting for select */ + sys_sem_t sem; +}; + +/** This struct is used to pass data to the set/getsockopt_internal + * functions running in tcpip_thread context (only a void* is allowed) */ +struct lwip_setgetsockopt_data { + /** socket struct for which to change options */ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + /** socket index for which to change options */ + int s; +#endif /* LWIP_DEBUG */ + /** level of the option to process */ + int level; + /** name of the option to process */ + int optname; + /** set: value to set the option to + * get: value of the option is stored here */ + void *optval; + /** size of *optval */ + socklen_t *optlen; + /** if an error occures, it is temporarily stored here */ + err_t err; +}; + +/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ + * sockaddr_in6 if instantiated. + */ +union sockaddr_aligned { + struct sockaddr sa; +#if LWIP_IPV6 + struct sockaddr_in6 sin6; +#endif /* LWIP_IPV6 */ + struct sockaddr_in sin; +}; + + +/** The global array of available sockets */ +static struct lwip_sock sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; +/** This counter is increased from lwip_select when the list is chagned + and checked in event_callback to see if it has changed. */ +static volatile int select_cb_ctr; + +/** Table to quickly map an lwIP error (err_t) to a socket error + * by using -err as an index */ +static const int err_to_errno_table[] = { + 0, /* ERR_OK 0 No error, everything OK. */ + ENOMEM, /* ERR_MEM -1 Out of memory error. */ + ENOBUFS, /* ERR_BUF -2 Buffer error. */ + EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */ + EINVAL, /* ERR_VAL -6 Illegal value. */ + EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ISCONN -9 Already connected. */ + ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */ + ECONNRESET, /* ERR_RST -11 Connection reset. */ + ENOTCONN, /* ERR_CLSD -12 Connection closed. */ + ENOTCONN, /* ERR_CONN -13 Not connected. */ + EIO, /* ERR_ARG -14 Illegal argument. */ + -1, /* ERR_IF -15 Low-level netif error */ +}; + +#define ERR_TO_ERRNO_TABLE_SIZE \ + (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0])) + +#define err_to_errno(err) \ + ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \ + err_to_errno_table[-(err)] : EIO) + +#ifdef ERRNO +#ifndef set_errno +#define set_errno(err) errno = (err) +#endif +#else /* ERRNO */ +#define set_errno(err) +#endif /* ERRNO */ + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward delcaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +static void lwip_getsockopt_internal(void *arg); +static void lwip_setsockopt_internal(void *arg); + +/** + * Initialize this module. This function has to be called before any other + * functions in this module! + */ +void +lwip_socket_init(void) +{ +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +get_socket(int s) +{ + struct lwip_sock *sock; + + if ((s < 0) || (s >= NUM_SOCKETS)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + set_errno(EBADF); + return NULL; + } + + sock = &sockets[s]; + + if (!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s)); + set_errno(EBADF); + return NULL; + } + + return sock; +} + +/** + * Same as get_socket but doesn't set errno + * + * @param s externally used socket index + * @return struct lwip_sock for the socket or NULL if not found + */ +static struct lwip_sock * +tryget_socket(int s) +{ + if ((s < 0) || (s >= NUM_SOCKETS)) { + return NULL; + } + if (!sockets[s].conn) { + return NULL; + } + return &sockets[s]; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @param accepted 1 if socket has been created by accept(), + * 0 if socket has been created by socket() + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn, int accepted) +{ + int i; + SYS_ARCH_DECL_PROTECT(lev); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + if (!sockets[i].conn) { + sockets[i].conn = newconn; + /* The socket is not yet known to anyone, so no need to protect + after having marked it as used. */ + SYS_ARCH_UNPROTECT(lev); + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + /* TCP sendbuf is empty, but the socket is not yet writable until connected + * (unless it has been created by accept()). */ + sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1); + sockets[i].errevent = 0; + sockets[i].err = 0; + sockets[i].select_waiting = 0; + return i; + } + SYS_ARCH_UNPROTECT(lev); + } + return -1; +} + +/** Free a socket. The socket's netconn must have been + * delete before! + * + * @param sock the socket to free + * @param is_tcp != 0 for TCP sockets, used to free lastdata + */ +static void +free_socket(struct lwip_sock *sock, int is_tcp) +{ + void *lastdata; + SYS_ARCH_DECL_PROTECT(lev); + + lastdata = sock->lastdata; + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->err = 0; + + /* Protect socket array */ + SYS_ARCH_PROTECT(lev); + sock->conn = NULL; + SYS_ARCH_UNPROTECT(lev); + /* don't use 'sock' after this line, as another task might have allocated it */ + + if (lastdata != NULL) { + if (is_tcp) { + pbuf_free((struct pbuf *)lastdata); + } else { + netbuf_delete((struct netbuf *)lastdata); + } + } +} + +/* Below this, the well-known socket functions are implemented. + * Use google.com or opengroup.org to get a good description :-) + * + * Exceptions are documented! + */ + +int +lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + struct lwip_sock *sock, *nsock; + struct netconn *newconn; + ipX_addr_t naddr; + u16_t port = 0; + int newsock; + err_t err; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* wait for a new connection */ + err = netconn_accept(sock->conn, &newconn); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("newconn != NULL", newconn != NULL); + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(newconn, 1); + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (addr != NULL) { + union sockaddr_aligned tempaddr; + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, ipX_2_ip(&naddr), &port); + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err)); + netconn_delete(newconn); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port); + if (*addrlen > tempaddr.sa.sa_len) { + *addrlen = tempaddr.sa.sa_len; + } + MEMCPY(addr, &tempaddr, *addrlen); + } + + newsock = alloc_socket(newconn, 1); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback); + nsock = &sockets[newsock]; + + /* See event_callback: If data comes in right away after an accept, even + * though the server task might not have created a new socket yet. + * In that case, newconn->socket is counted down (newconn->socket--), + * so nsock->rcvevent is >= 1 here! + */ + SYS_ARCH_PROTECT(lev); + nsock->rcvevent += (s16_t)(-1 - newconn->socket); + newconn->socket = newsock; + SYS_ARCH_UNPROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock)); + if (addr != NULL) { + LWIP_DEBUGF(SOCKETS_DEBUG, (" addr=")); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); + } + + sock_set_errno(sock, 0); + return newsock; +} + +int +lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + ipX_addr_t local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); + + err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_close(int s) +{ + struct lwip_sock *sock; + int is_tcp = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if(sock->conn != NULL) { + is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP; + } else { + LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL); + } + + netconn_delete(sock->conn); + + free_socket(sock, is_tcp); + set_errno(0); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_sock *sock; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, familiy and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + if (name->sa_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ipX_addr_t remote_addr; + u16_t remote_port; + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); + + err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port); + } + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err)); + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s)); + sock_set_errno(sock, 0); + return 0; +} + +/** + * Set a socket into listen mode. + * The socket may not have been used for another connection previously. + * + * @param s the socket to set to listening mode + * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_sock *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* limit the "backlog" parameter to fit in an u8_t */ + backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff); + + err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog); + + if (err != ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + struct lwip_sock *sock; + void *buf = NULL; + struct pbuf *p; + u16_t buflen, copylen; + int off = 0; + u8_t done = 0; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags)); + sock = get_socket(s); + if (!sock) { + return -1; + } + + do { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata)); + /* Check if there is data left from the last recv operation. */ + if (sock->lastdata) { + buf = sock->lastdata; + } else { + /* If this is non-blocking call, then check first */ + if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + /* No data was left from the previous operation, so we try to get + some from the network. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf); + } else { + err = netconn_recv(sock->conn, (struct netbuf **)&buf); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n", + err, buf)); + + if (err != ERR_OK) { + if (off > 0) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + /* already received data, return that */ + sock_set_errno(sock, 0); + return off; + } + /* We should really do some error checking here. */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n", + s, lwip_strerr(err))); + sock_set_errno(sock, err_to_errno(err)); + if (err == ERR_CLSD) { + return 0; + } else { + return -1; + } + } + LWIP_ASSERT("buf != NULL", buf != NULL); + sock->lastdata = buf; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + p = (struct pbuf *)buf; + } else { + p = ((struct netbuf *)buf)->p; + } + buflen = p->tot_len; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n", + buflen, len, off, sock->lastoffset)); + + buflen -= sock->lastoffset; + + if (len > buflen) { + copylen = buflen; + } else { + copylen = (u16_t)len; + } + + /* copy the contents of the received buffer into + the supplied memory pointer mem */ + pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset); + + off += copylen; + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen); + len -= copylen; + if ( (len <= 0) || + (p->flags & PBUF_FLAG_PUSH) || + (sock->rcvevent <= 0) || + ((flags & MSG_PEEK)!=0)) { + done = 1; + } + } else { + done = 1; + } + + /* Check to see from where the data was.*/ + if (done) { +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { + u16_t port; + ipX_addr_t tmpaddr; + ipX_addr_t *fromaddr; + union sockaddr_aligned saddr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr = &tmpaddr; + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0); + } else { + port = netbuf_fromport((struct netbuf *)buf); + fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf); + } + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, fromaddr, port); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, fromaddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#if SOCKETS_DEBUG + if (from && fromlen) +#endif /* SOCKETS_DEBUG */ + { + if (*fromlen > saddr.sa.sa_len) { + *fromlen = saddr.sa.sa_len; + } + MEMCPY(from, &saddr, *fromlen); + } + } + } + + /* If we don't peek the incoming message... */ + if ((flags & MSG_PEEK) == 0) { + /* If this is a TCP socket, check if there is data left in the + buffer. If so, it should be saved in the sock structure for next + time around. */ + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) { + sock->lastdata = buf; + sock->lastoffset += copylen; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + } + } + } while (!done); + + if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) { + /* update receive window */ + netconn_recved(sock->conn, (u32_t)off); + } + sock_set_errno(sock, 0); + return off; +} + +int +lwip_read(int s, void *mem, size_t len) +{ + return lwip_recvfrom(s, mem, len, 0, NULL, NULL); +} + +int +lwip_recv(int s, void *mem, size_t len, int flags) +{ + return lwip_recvfrom(s, mem, len, flags, NULL, NULL); +} + +int +lwip_send(int s, const void *data, size_t size, int flags) +{ + struct lwip_sock *sock; + err_t err; + u8_t write_flags; + size_t written; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n", + s, data, size, flags)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { +#if (LWIP_UDP || LWIP_RAW) + return lwip_sendto(s, data, size, flags, NULL, 0); +#else /* (LWIP_UDP || LWIP_RAW) */ + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)written : -1); +} + +int +lwip_sendto(int s, const void *data, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + struct lwip_sock *sock; + err_t err; + u16_t short_size; + u16_t remote_port; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; +#endif + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { +#if LWIP_TCP + return lwip_send(s, data, size, flags); +#else /* LWIP_TCP */ + LWIP_UNUSED_ARG(flags); + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* @todo: split into multiple sendto's? */ + LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff); + short_size = (u16_t)size; + LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) || + (IS_SOCK_ADDR_LEN_VALID(tolen) && + IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(tolen); + +#if LWIP_TCPIP_CORE_LOCKING + /* Special speedup for fast UDP/RAW sending: call the raw API directly + instead of using the netconn functions. */ + { + struct pbuf* p; + ipX_addr_t *remote_addr; + ipX_addr_t remote_addr_tmp; + +#if LWIP_NETIF_TX_SINGLE_PBUF + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); + if (p != NULL) { +#if LWIP_CHECKSUM_ON_COPY + u16_t chksum = 0; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { + chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + MEMCPY(p->payload, data, size); +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF); + if (p != NULL) { + p->payload = (void*)data; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + if (to != NULL) { + SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6, + to, &remote_addr_tmp, remote_port); + remote_addr = &remote_addr_tmp; + } else { + remote_addr = &sock->conn->pcb.ip->remote_ip; +#if LWIP_UDP + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { + remote_port = sock->conn->pcb.udp->remote_port; + } else +#endif /* LWIP_UDP */ + { + remote_port = 0; + } + } + + LOCK_TCPIP_CORE(); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { +#if LWIP_RAW + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr)); +#else /* LWIP_RAW */ + err = ERR_ARG; +#endif /* LWIP_RAW */ + } +#if LWIP_UDP && LWIP_RAW + else +#endif /* LWIP_UDP && LWIP_RAW */ + { +#if LWIP_UDP +#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF + err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, + ipX_2_ip(remote_addr), remote_port, 1, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ + err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, + ipX_2_ip(remote_addr), remote_port); +#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ +#else /* LWIP_UDP */ + err = ERR_ARG; +#endif /* LWIP_UDP */ + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + /* initialize a buffer */ + buf.p = buf.ptr = NULL; +#if LWIP_CHECKSUM_ON_COPY + buf.flags = 0; +#endif /* LWIP_CHECKSUM_ON_COPY */ + if (to) { + SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port); + } else { + remote_port = 0; + ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); + } + netbuf_fromport(&buf) = remote_port; + + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &buf.addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port)); + + /* make the buffer point to the data that should be sent */ +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Allocate a new netbuf and copy the data into it. */ + if (netbuf_alloc(&buf, short_size) == NULL) { + err = ERR_MEM; + } else { +#if LWIP_CHECKSUM_ON_COPY + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) { + u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); + netbuf_set_chksum(&buf, chksum); + err = ERR_OK; + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + err = netbuf_take(&buf, data, short_size); + } + } +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + err = netbuf_ref(&buf, data, short_size); +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (err == ERR_OK) { + /* send the data */ + err = netconn_send(sock->conn, &buf); + } + + /* deallocated the buffer */ + netbuf_free(&buf); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? short_size : -1); +} + +int +lwip_socket(int domain, int type, int protocol) +{ + struct netconn *conn; + int i; + +#if !LWIP_IPV6 + LWIP_UNUSED_ARG(domain); /* @todo: check this */ +#endif /* LWIP_IPV6 */ + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW), + (u8_t)protocol, event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_DGRAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, + ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) , + event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + break; + case SOCK_STREAM: + conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ", + domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol)); + if (conn != NULL) { + /* Prevent automatic window updates, we do this on our own! */ + netconn_set_noautorecved(conn, 1); + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n", + domain, type, protocol)); + set_errno(EINVAL); + return -1; + } + + if (!conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n")); + set_errno(ENOBUFS); + return -1; + } + + i = alloc_socket(conn, 0); + + if (i == -1) { + netconn_delete(conn); + set_errno(ENFILE); + return -1; + } + conn->socket = i; + LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i)); + set_errno(0); + return i; +} + +int +lwip_write(int s, const void *data, size_t size) +{ + return lwip_send(s, data, size, 0); +} + +/** + * Go through the readset and writeset lists and see which socket of the sockets + * set in the sets has events. On return, readset, writeset and exceptset have + * the sockets enabled that had events. + * + * exceptset is not used for now!!! + * + * @param maxfdp1 the highest socket index in the sets + * @param readset_in: set of sockets to check for read events + * @param writeset_in: set of sockets to check for write events + * @param exceptset_in: set of sockets to check for error events + * @param readset_out: set of sockets that had read events + * @param writeset_out: set of sockets that had write events + * @param exceptset_out: set os sockets that had error events + * @return number of sockets that had events (read/write/exception) (>= 0) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in, + fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_sock *sock; + SYS_ARCH_DECL_PROTECT(lev); + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + for(i = 0; i < maxfdp1; i++) { + void* lastdata = NULL; + s16_t rcvevent = 0; + u16_t sendevent = 0; + u16_t errevent = 0; + /* First get the socket's status (protected)... */ + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + lastdata = sock->lastdata; + rcvevent = sock->rcvevent; + sendevent = sock->sendevent; + errevent = sock->errevent; + } + SYS_ARCH_UNPROTECT(lev); + /* ... then examine it: */ + /* See if netconn of this socket is ready for read */ + if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + /* See if netconn of this socket is ready for write */ + if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + /* See if netconn of this socket had an error */ + if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) { + FD_SET(i, &lexceptset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i)); + nready++; + } + } + /* copy local sets to the ones provided as arguments */ + *readset_out = lreadset; + *writeset_out = lwriteset; + *exceptset_out = lexceptset; + + LWIP_ASSERT("nready >= 0", nready >= 0); + return nready; +} + +/** + * Processing exceptset is not yet implemented. + */ +int +lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout) +{ + u32_t waitres = 0; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + err_t err; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (s32_t)timeout->tv_sec : (s32_t)-1, + timeout ? (s32_t)timeout->tv_usec : (s32_t)-1)); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + + /* If we don't have any current events, then suspend if we are supposed to */ + if (!nready) { + if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* None ready: add our semaphore to list: + We don't actually need any dynamic memory. Our entry on the + list is only valid while we are in this function, so it's ok + to use local variables. */ + + select_cb.next = NULL; + select_cb.prev = NULL; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + err = sys_sem_new(&select_cb.sem, 0); + if (err != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } + + /* Protect the select_cb_list */ + SYS_ARCH_PROTECT(lev); + + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + if (select_cb_list != NULL) { + select_cb_list->prev = &select_cb; + } + select_cb_list = &select_cb; + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + + /* Now we can safely unprotect */ + SYS_ARCH_UNPROTECT(lev); + + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + SYS_ARCH_UNPROTECT(lev); + } + } + + /* Call lwip_selscan again: there could have been events between + the last scan (whithout us on the list) and putting us on the list! */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + if (!nready) { + /* Still none ready, just wait to be woken */ + if (timeout == 0) { + /* Wait forever */ + msectimeout = 0; + } else { + msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000)); + if (msectimeout == 0) { + /* Wait 1ms at least (0 means wait forever) */ + msectimeout = 1; + } + } + + waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout); + } + /* Increase select_waiting for each socket we are interested in */ + for(i = 0; i < maxfdp1; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock = tryget_socket(i); + LWIP_ASSERT("sock != NULL", sock != NULL); + SYS_ARCH_PROTECT(lev); + sock->select_waiting--; + LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0); + SYS_ARCH_UNPROTECT(lev); + } + } + /* Take us off the list */ + SYS_ARCH_PROTECT(lev); + if (select_cb.next != NULL) { + select_cb.next->prev = select_cb.prev; + } + if (select_cb_list == &select_cb) { + LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL); + select_cb_list = select_cb.next; + } else { + LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL); + select_cb.prev->next = select_cb.next; + } + /* Increasing this counter tells even_callback that the list has changed. */ + select_cb_ctr++; + SYS_ARCH_UNPROTECT(lev); + + sys_sem_free(&select_cb.sem); + if (waitres == SYS_ARCH_TIMEOUT) { + /* Timeout */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + /* This is OK as the local fdsets are empty and nready is zero, + or we would have returned earlier. */ + goto return_copy_fdsets; + } + + /* See what's set */ + nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset); + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); +return_copy_fdsets: + set_errno(0); + if (readset) { + *readset = lreadset; + } + if (writeset) { + *writeset = lwriteset; + } + if (exceptset) { + *exceptset = lexceptset; + } + return nready; +} + +/** + * Callback registered in the netconn layer for each socket-netconn. + * Processes recvevent (data available) and wakes up tasks waiting for select. + */ +static void +event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) +{ + int s; + struct lwip_sock *sock; + struct lwip_select_cb *scb; + int last_select_cb_ctr; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(len); + + /* Get socket */ + if (conn) { + s = conn->socket; + if (s < 0) { + /* Data comes in right away after an accept, even though + * the server task might not have created a new socket yet. + * Just count down (or up) if that's the case and we + * will use the data later. Note that only receive events + * can happen before the new socket is set up. */ + SYS_ARCH_PROTECT(lev); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + SYS_ARCH_UNPROTECT(lev); + return; + } + s = conn->socket; + SYS_ARCH_UNPROTECT(lev); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + SYS_ARCH_PROTECT(lev); + /* Set event as required */ + switch (evt) { + case NETCONN_EVT_RCVPLUS: + sock->rcvevent++; + break; + case NETCONN_EVT_RCVMINUS: + sock->rcvevent--; + break; + case NETCONN_EVT_SENDPLUS: + sock->sendevent = 1; + break; + case NETCONN_EVT_SENDMINUS: + sock->sendevent = 0; + break; + case NETCONN_EVT_ERROR: + sock->errevent = 1; + break; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + + if (sock->select_waiting == 0) { + /* noone is waiting for this socket, no need to check select_cb_list */ + SYS_ARCH_UNPROTECT(lev); + return; + } + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code goes through the select_cb_list list multiple times + ONLY IF a select was actually waiting. We go through the list the number + of waiting select calls + 1. This list is expected to be small. */ + + /* At this point, SYS_ARCH is still protected! */ +again: + for (scb = select_cb_list; scb != NULL; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* semaphore not signalled yet */ + int do_signal = 0; + /* Test this select call for our socket */ + if (sock->rcvevent > 0) { + if (scb->readset && FD_ISSET(s, scb->readset)) { + do_signal = 1; + } + } + if (sock->sendevent != 0) { + if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) { + do_signal = 1; + } + } + if (sock->errevent != 0) { + if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) { + do_signal = 1; + } + } + if (do_signal) { + scb->sem_signalled = 1; + /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might + lead to the select thread taking itself off the list, invalidagin the semaphore. */ + sys_sem_signal(&scb->sem); + } + } + /* unlock interrupts with each step */ + last_select_cb_ctr = select_cb_ctr; + SYS_ARCH_UNPROTECT(lev); + /* this makes sure interrupt protection time is short */ + SYS_ARCH_PROTECT(lev); + if (last_select_cb_ctr != select_cb_ctr) { + /* someone has changed select_cb_list, restart at the beginning */ + goto again; + } + } + SYS_ARCH_UNPROTECT(lev); +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + } else { + sock_set_errno(sock, ENOTCONN); + return ENOTCONN; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if(how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return EINVAL; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_sock *sock; + union sockaddr_aligned saddr; + ipX_addr_t naddr; + u16_t port; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* get the IP address and port */ + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local); + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, &naddr, port); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); + + if (*namelen > saddr.sa.sa_len) { + *namelen = saddr.sa.sa_len; + } + MEMCPY(name, &saddr, *namelen); + + sock_set_errno(sock, 0); + return 0; +} + +int +lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 0); +} + +int +lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return lwip_getaddrname(s, name, namelen, 1); +} + +int +lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + err_t err = ERR_OK; + struct lwip_sock *sock = get_socket(s); + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_ERROR: + case SO_KEEPALIVE: + /* UNIMPL case SO_CONTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + case SO_TYPE: + /* UNIMPL case SO_USELOOPBACK: */ + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; + + case SO_NO_CHECK: + if (*optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + break; + case IP_MULTICAST_IF: + if (*optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + break; + case IP_MULTICAST_LOOP: + if (*optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*optlen < sizeof(int)) { + err = EINVAL; + } + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (*optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return 0; + } + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE*/ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = optval; + data.optlen = optlen; + data.err = err; + tcpip_callback(lwip_getsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_getsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_getsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_ACCEPTCONN: + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /*case SO_USELOOPBACK: UNIMPL */ + *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + case SO_TYPE: + switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { + case NETCONN_RAW: + *(int*)optval = SOCK_RAW; + break; + case NETCONN_TCP: + *(int*)optval = SOCK_STREAM; + break; + case NETCONN_UDP: + *(int*)optval = SOCK_DGRAM; + break; + default: /* unrecognized socket type */ + *(int*)optval = netconn_type(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (netconn_type(sock->conn)) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + /* only overwrite ERR_OK or tempoary errors */ + if ((sock->err == 0) || (sock->err == EINPROGRESS)) { + sock_set_errno(sock, err_to_errno(sock->conn->last_err)); + } + *(int *)optval = sock->err; + sock->err = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + *(int *)optval = netconn_get_sendtimeout(sock->conn); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + *(int *)optval = netconn_get_recvtimeout(sock->conn); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + *(int*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_TOS: + *(int*)optval = sock->conn->pcb.ip->tos; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", + s, *(int *)optval)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + *(u8_t*)optval = sock->conn->pcb.ip->ttl; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n", + s, *(int *)optval)); + break; + case IP_MULTICAST_IF: + inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; + case IP_MULTICAST_LOOP: + if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) { + *(u8_t*)optval = 1; + } else { + *(u8_t*)optval = 0; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n", + s, (*(int*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n", + s, *(int *)optval)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPINTVL: + *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n", + s, *(int *)optval)); + break; + case TCP_KEEPCNT: + *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", + s, *(int *)optval)); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_tx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + *(int*)optval = sock->conn->pcb.udp->chksum_len_rx; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + struct lwip_sock *sock = get_socket(s); + err_t err = ERR_OK; + struct lwip_setgetsockopt_data data; + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + + /* Do length and type checks for the various options first, to keep it readable. */ + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case case SO_CONTIMEO: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: +#endif /* LWIP_SO_RCVBUF */ + /* UNIMPL case SO_OOBINLINE: */ + /* UNIMPL case SO_SNDBUF: */ + /* UNIMPL case SO_RCVLOWAT: */ + /* UNIMPL case SO_SNDLOWAT: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; + case SO_NO_CHECK: + if (optlen < sizeof(int)) { + err = EINVAL; + } +#if LWIP_UDP + if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) || + ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) { + /* this flag is only available for UDP, not for UDP lite */ + err = EAFNOSUPPORT; + } +#endif /* LWIP_UDP */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + /* UNIMPL case IP_HDRINCL: */ + /* UNIMPL case IP_RCVDSTADDR: */ + /* UNIMPL case IP_RCVIF: */ + case IP_TTL: + case IP_TOS: + if (optlen < sizeof(int)) { + err = EINVAL; + } + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_IF: + if (optlen < sizeof(struct in_addr)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_MULTICAST_LOOP: + if (optlen < sizeof(u8_t)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + if (optlen < sizeof(struct ip_mreq)) { + err = EINVAL; + } + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + err = EAFNOSUPPORT; + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no TCP socket, ignore any options. */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + switch (optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + case TCP_KEEPINTVL: + case TCP_KEEPCNT: +#endif /* LWIP_TCP_KEEPALIVE */ + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (optlen < sizeof(int)) { + err = EINVAL; + } + + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) + return 0; + + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + if (optlen < sizeof(int)) { + err = EINVAL; + break; + } + + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) + return 0; + + switch (optname) { + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP && LWIP_UDPLITE */ +/* UNDEFINED LEVEL */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + } /* switch (level) */ + + + if (err != ERR_OK) { + sock_set_errno(sock, err); + return -1; + } + + + /* Now do the actual option processing */ + data.sock = sock; +#ifdef LWIP_DEBUG + data.s = s; +#endif /* LWIP_DEBUG */ + data.level = level; + data.optname = optname; + data.optval = (void*)optval; + data.optlen = &optlen; + data.err = err; + tcpip_callback(lwip_setsockopt_internal, &data); + sys_arch_sem_wait(&sock->conn->op_completed, 0); + /* maybe lwip_setsockopt_internal has changed err */ + err = data.err; + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +static void +lwip_setsockopt_internal(void *arg) +{ + struct lwip_sock *sock; +#ifdef LWIP_DEBUG + int s; +#endif /* LWIP_DEBUG */ + int level, optname; + const void *optval; + struct lwip_setgetsockopt_data *data; + + LWIP_ASSERT("arg != NULL", arg != NULL); + + data = (struct lwip_setgetsockopt_data*)arg; + sock = data->sock; +#ifdef LWIP_DEBUG + s = data->s; +#endif /* LWIP_DEBUG */ + level = data->level; + optname = data->optname; + optval = data->optval; + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* The option flags */ + case SO_BROADCAST: + /* UNIMPL case SO_DEBUG: */ + /* UNIMPL case SO_DONTROUTE: */ + case SO_KEEPALIVE: + /* UNIMPL case SO_OOBINCLUDE: */ +#if SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ + /* UNIMPL case SO_USELOOPBACK: */ + if (*(int*)optval) { + ip_set_option(sock->conn->pcb.ip, optname); + } else { + ip_reset_option(sock->conn->pcb.ip, optname); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + netconn_set_recvtimeout(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + netconn_set_recvbufsize(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + if (*(int*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM); + } + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n", + s, sock->conn->pcb.ip->ttl)); + break; + case IP_TOS: + sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", + s, sock->conn->pcb.ip->tos)); + break; +#if LWIP_IGMP + case IP_MULTICAST_TTL: + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); + break; + case IP_MULTICAST_LOOP: + if (*(u8_t*)optval) { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP); + } else { + udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); + } + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* If this is a TCP or a RAW socket, ignore these options. */ + struct ip_mreq *imr = (struct ip_mreq *)optval; + ip_addr_t if_addr; + ip_addr_t multi_addr; + inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); + inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup(&if_addr, &multi_addr); + } else { + data->err = igmp_leavegroup(&if_addr, &multi_addr); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + switch (optname) { + case TCP_NODELAY: + if (*(int*)optval) { + tcp_nagle_disable(sock->conn->pcb.tcp); + } else { + tcp_nagle_enable(sock->conn->pcb.tcp); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", + s, (*(int *)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + +#if LWIP_TCP_KEEPALIVE + case TCP_KEEPIDLE: + sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_idle)); + break; + case TCP_KEEPINTVL: + sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_intvl)); + break; + case TCP_KEEPCNT: + sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n", + s, sock->conn->pcb.tcp->keep_cnt)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + if (*(int*)optval) { + sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; + } else { + sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", + s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + switch (optname) { + case UDPLITE_SEND_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + case UDPLITE_RECV_CSCOV: + if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + default: + LWIP_ASSERT("unhandled optname", 0); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + default: + LWIP_ASSERT("unhandled level", 0); + break; + } /* switch (level) */ + sys_sem_signal(&sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_sock *sock = get_socket(s); + u8_t val; +#if LWIP_SO_RCVBUF + u16_t buflen = 0; + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } +#if LWIP_FIONREAD_LINUXMODE + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + struct pbuf *p; + if (sock->lastdata) { + p = ((struct netbuf *)sock->lastdata)->p; + } else { + struct netbuf *rxbuf; + err_t err; + if (sock->rcvevent <= 0) { + *((u16_t*)argp) = 0; + } else { + err = netconn_recv(sock->conn, &rxbuf); + if (err != ERR_OK) { + *((u16_t*)argp) = 0; + } else { + sock->lastdata = rxbuf; + *((u16_t*)argp) = rxbuf->p->tot_len; + } + } + } + return 0; + } +#endif /* LWIP_FIONREAD_LINUXMODE */ + +#if LWIP_SO_RCVBUF + /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((u16_t*)argp) = (u16_t)recv_avail; + + /* Check if there is data left from the last recv operation. /maq 041215 */ + if (sock->lastdata) { + struct pbuf *p = (struct pbuf *)sock->lastdata; + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((u16_t*)argp) += buflen; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp))); + sock_set_errno(sock, 0); + return 0; +#else /* LWIP_SO_RCVBUF */ + break; +#endif /* LWIP_SO_RCVBUF */ +#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ + + case FIONBIO: + val = 0; + if (argp && *(u32_t*)argp) { + val = 1; + } + netconn_set_nonblocking(sock->conn, val); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val)); + sock_set_errno(sock, 0); + return 0; + + default: + break; + } /* switch (cmd) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + return -1; +} + +/** A minimal implementation of fcntl. + * Currently only the commands F_GETFL and F_SETFL are implemented. + * Only the flag O_NONBLOCK is implemented. + */ +int +lwip_fcntl(int s, int cmd, int val) +{ + struct lwip_sock *sock = get_socket(s); + int ret = -1; + + if (!sock || !sock->conn) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + break; + case F_SETFL: + if ((val & ~O_NONBLOCK) == 0) { + /* only O_NONBLOCK, all other bits are zero */ + netconn_set_nonblocking(sock->conn, val & O_NONBLOCK); + ret = 0; + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + break; + } + return ret; +} + +#endif /* LWIP_SOCKET */ diff --git a/external/badvpn_dns/lwip/src/api/tcpip.c b/external/badvpn_dns/lwip/src/api/tcpip.c new file mode 100644 index 00000000..7c1c9cad --- /dev/null +++ b/external/badvpn_dns/lwip/src/api/tcpip.c @@ -0,0 +1,492 @@ +/** + * @file + * Sequential API Main thread module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/memp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static tcpip_init_done_fn tcpip_init_done; +static void *tcpip_init_done_arg; +static sys_mbox_t mbox; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_mutex_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + +/** + * The main lwIP thread. This thread has exclusive access to lwIP core functions + * (unless access to them is not locked). Other threads communicate with this + * thread using message boxes. + * + * It also starts all the timers to make sure they are running in the right + * thread context. + * + * @param arg unused argument + */ +static void +tcpip_thread(void *arg) +{ + struct tcpip_msg *msg; + LWIP_UNUSED_ARG(arg); + + if (tcpip_init_done != NULL) { + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + UNLOCK_TCPIP_CORE(); + LWIP_TCPIP_THREAD_ALIVE(); + /* wait for a message, timeouts are processed while waiting */ + sys_timeouts_mbox_fetch(&mbox, (void **)&msg); + LOCK_TCPIP_CORE(); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg)); + msg->msg.apimsg->function(&(msg->msg.apimsg->msg)); + break; +#endif /* LWIP_NETCONN */ + +#if !LWIP_TCPIP_CORE_LOCKING_INPUT + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ETHERNET + if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ETHERNET */ +#if LWIP_IPV6 + if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) { + ip6_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_IPV6 */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + +#if LWIP_NETIF_API + case TCPIP_MSG_NETIFAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg)); + msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg)); + break; +#endif /* LWIP_NETIF_API */ + +#if LWIP_TCPIP_TIMEOUT + case TCPIP_MSG_TIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg)); + sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + case TCPIP_MSG_UNTIMEOUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg)); + sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; +#endif /* LWIP_TCPIP_TIMEOUT */ + + case TCPIP_MSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + + default: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type)); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + break; + } + } +} + +/** + * Pass a received packet to tcpip_thread for input processing + * + * @param p the received packet, p->payload pointing to the Ethernet header or + * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or + * NETIF_FLAG_ETHERNET flags) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ +#if LWIP_TCPIP_CORE_LOCKING_INPUT + err_t ret; + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp)); + LOCK_TCPIP_CORE(); +#if LWIP_ETHERNET + if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) { + ret = ethernet_input(p, inp); + } else +#endif /* LWIP_ETHERNET */ + { + ret = ip_input(p, inp); + } + UNLOCK_TCPIP_CORE(); + return ret; +#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */ + struct tcpip_msg *msg; + + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_INPKT; + msg->msg.inp.p = p; + msg->msg.inp.netif = inp; + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + return ERR_MEM; + } + return ERR_OK; +#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */ +} + +/** + * Call a specific function in the thread context of + * tcpip_thread for easy access synchronization. + * A function called in that way may access lwIP core code + * without fearing concurrent access. + * + * @param f the function to call + * @param ctx parameter passed to f + * @param block 1 to block until the request is posted, 0 to non-blocking mode + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + if (block) { + sys_mbox_post(&mbox, msg); + } else { + if (sys_mbox_trypost(&mbox, msg) != ERR_OK) { + memp_free(MEMP_TCPIP_MSG_API, msg); + return ERR_MEM; + } + } + return ERR_OK; + } + return ERR_VAL; +} + +#if LWIP_TCPIP_TIMEOUT +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_TIMEOUT; + msg->msg.tmo.msecs = msecs; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} + +/** + * call sys_untimeout in tcpip_thread + * + * @param msec time in milliseconds for timeout + * @param h function to be called on timeout + * @param arg argument to pass to timeout function h + * @return ERR_MEM on memory error, ERR_OK otherwise + */ +err_t +tcpip_untimeout(sys_timeout_handler h, void *arg) +{ + struct tcpip_msg *msg; + + if (sys_mbox_valid(&mbox)) { + msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_UNTIMEOUT; + msg->msg.tmo.h = h; + msg->msg.tmo.arg = arg; + sys_mbox_post(&mbox, msg); + return ERR_OK; + } + return ERR_VAL; +} +#endif /* LWIP_TCPIP_TIMEOUT */ + +#if LWIP_NETCONN +/** + * Call the lower part of a netconn_* function + * This function is then running in the thread context + * of tcpip_thread and has exclusive access to lwIP core code. + * + * @param apimsg a struct containing the function to call and its parameters + * @return ERR_OK if the function was called, another err_t if not + */ +err_t +tcpip_apimsg(struct api_msg *apimsg) +{ + struct tcpip_msg msg; +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + if (sys_mbox_valid(&mbox)) { + msg.type = TCPIP_MSG_API; + msg.msg.apimsg = apimsg; + sys_mbox_post(&mbox, &msg); + sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0); + return apimsg->msg.err; + } + return ERR_VAL; +} + +#endif /* LWIP_NETCONN */ + +#if LWIP_NETIF_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a netifapi_* + * function. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_netifapi(struct netifapi_msg* netifapimsg) +{ + struct tcpip_msg msg; + + if (sys_mbox_valid(&mbox)) { + err_t err = sys_sem_new(&netifapimsg->msg.sem, 0); + if (err != ERR_OK) { + netifapimsg->msg.err = err; + return err; + } + + msg.type = TCPIP_MSG_NETIFAPI; + msg.msg.netifapimsg = netifapimsg; + sys_mbox_post(&mbox, &msg); + sys_sem_wait(&netifapimsg->msg.sem); + sys_sem_free(&netifapimsg->msg.sem); + return netifapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a netifapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param netifapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_netifapi()) + */ +err_t +tcpip_netifapi_lock(struct netifapi_msg* netifapimsg) +{ + LOCK_TCPIP_CORE(); + netifapimsg->function(&(netifapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return netifapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + return sys_mbox_trypost(&mbox, msg); +} + +/** + * Initialize this module: + * - initialize all sub modules + * - start the tcpip_thread + * + * @param initfunc a function to call when tcpip_thread is running and finished initializing + * @param arg argument to pass to initfunc + */ +void +tcpip_init(tcpip_init_done_fn initfunc, void *arg) +{ + lwip_init(); + + tcpip_init_done = initfunc; + tcpip_init_done_arg = arg; + if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) { + LWIP_ASSERT("failed to create tcpip_thread mbox", 0); + } +#if LWIP_TCPIP_CORE_LOCKING + if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) { + LWIP_ASSERT("failed to create lock_tcpip_core", 0); + } +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO); +} + +/** + * Simple callback function used with tcpip_callback to free a pbuf + * (pbuf_free has a wrong signature for tcpip_callback) + * + * @param p The pbuf (chain) to be dereferenced. + */ +static void +pbuf_free_int(void *p) +{ + struct pbuf *q = (struct pbuf *)p; + pbuf_free(q); +} + +/** + * A simple wrapper function that allows you to free a pbuf from interrupt context. + * + * @param p The pbuf (chain) to be dereferenced. + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +pbuf_free_callback(struct pbuf *p) +{ + return tcpip_callback_with_block(pbuf_free_int, p, 0); +} + +/** + * A simple wrapper function that allows you to free heap memory from + * interrupt context. + * + * @param m the heap memory to free + * @return ERR_OK if callback could be enqueued, an err_t if not + */ +err_t +mem_free_callback(void *m) +{ + return tcpip_callback_with_block(mem_free, m, 0); +} + +#endif /* !NO_SYS */ diff --git a/external/badvpn_dns/lwip/src/core/def.c b/external/badvpn_dns/lwip/src/core/def.c new file mode 100644 index 00000000..352b5524 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/def.c @@ -0,0 +1,108 @@ +/** + * @file + * Common functions used throughout the stack. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ + +#include "lwip/opt.h" +#include "lwip/def.h" + +/** + * These are reference implementations of the byte swapping functions. + * Again with the aim of being simple, correct and fully portable. + * Byte swapping is the second thing you would want to optimize. You will + * need to port it to your architecture and in your cc.h: + * + * #define LWIP_PLATFORM_BYTESWAP 1 + * #define LWIP_PLATFORM_HTONS(x) + * #define LWIP_PLATFORM_HTONL(x) + * + * Note ntohs() and ntohl() are merely references to the htonx counterparts. + */ + +#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) + +/** + * Convert an u16_t from host- to network byte order. + * + * @param n u16_t in host byte order + * @return n in network byte order + */ +u16_t +lwip_htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +/** + * Convert an u16_t from network- to host byte order. + * + * @param n u16_t in network byte order + * @return n in host byte order + */ +u16_t +lwip_ntohs(u16_t n) +{ + return lwip_htons(n); +} + +/** + * Convert an u32_t from host- to network byte order. + * + * @param n u32_t in host byte order + * @return n in network byte order + */ +u32_t +lwip_htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); +} + +/** + * Convert an u32_t from network- to host byte order. + * + * @param n u32_t in network byte order + * @return n in host byte order + */ +u32_t +lwip_ntohl(u32_t n) +{ + return lwip_htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/external/badvpn_dns/lwip/src/core/dhcp.c b/external/badvpn_dns/lwip/src/core/dhcp.c new file mode 100644 index 00000000..21fd7845 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/dhcp.c @@ -0,0 +1,1771 @@ +/** + * @file + * Dynamic Host Configuration Protocol client + * + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + +/** Default for DHCP_GLOBAL_XID is 0xABCD0000 + * This can be changed by defining DHCP_GLOBAL_XID and DHCP_GLOBAL_XID_HEADER, e.g. + * #define DHCP_GLOBAL_XID_HEADER "stdlib.h" + * #define DHCP_GLOBAL_XID rand() + */ +#ifdef DHCP_GLOBAL_XID_HEADER +#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */ +#endif + +/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU + * MTU is checked to be big enough in dhcp_start */ +#define DHCP_MAX_MSG_LEN(netif) (netif->mtu) +#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576 +/** Minimum length for reply before packet is parsed */ +#define DHCP_MIN_REPLY_LEN 44 + +#define REBOOT_TRIES 2 + +/** Option handling: options are parsed in dhcp_parse_reply + * and saved in an array where other functions can load them from. + * This might be moved into the struct dhcp (not necessarily since + * lwIP is single-threaded and the array is only used while in recv + * callback). */ +#define DHCP_OPTION_IDX_OVERLOAD 0 +#define DHCP_OPTION_IDX_MSG_TYPE 1 +#define DHCP_OPTION_IDX_SERVER_ID 2 +#define DHCP_OPTION_IDX_LEASE_TIME 3 +#define DHCP_OPTION_IDX_T1 4 +#define DHCP_OPTION_IDX_T2 5 +#define DHCP_OPTION_IDX_SUBNET_MASK 6 +#define DHCP_OPTION_IDX_ROUTER 7 +#define DHCP_OPTION_IDX_DNS_SERVER 8 +#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS) + +/** Holds the decoded option values, only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX]; +/** Holds a flag which option was received and is contained in dhcp_rx_options_val, + only valid while in dhcp_recv. + @todo: move this into struct dhcp? */ +u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX]; + +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + +#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0) +#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1) +#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0) +#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given))) +#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx]) +#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val)) + + +/* DHCP client state machine functions */ +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_bind(struct netif *netif); +#if DHCP_DOES_ARP_CHECK +static err_t dhcp_decline(struct netif *netif); +#endif /* DHCP_DOES_ARP_CHECK */ +static err_t dhcp_rebind(struct netif *netif); +static err_t dhcp_reboot(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/* receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); + +/* set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/* build outgoing messages */ +/* create a DHCP message, fill in common headers */ +static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type); +/* free a DHCP request */ +static void dhcp_delete_msg(struct dhcp *dhcp); +/* add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/* add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ +/* always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We clear any existing set IP address and restart DHCP negotiation + * afresh (as per RFC2131 3.2.3). + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_nak(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Set the interface down since the address must no longer be used, as per RFC2131 */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + /* Change to a defined state */ + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* We can immediately restart discovery */ + dhcp_discover(netif); +} + +#if DHCP_DOES_ARP_CHECK +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + dhcp_set_state(dhcp, DHCP_CHECKING); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); +} +#endif /* DHCP_DOES_ARP_CHECK */ + +/** + * Remember the configuration offered by a DHCP server. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* obtain the server address */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) { + ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID))); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->server_ip_addr))); + /* remember offered address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif)); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp_set_state(dhcp, DHCP_REQUESTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr))); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* send broadcast to any DHCP server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + */ +void +dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + */ +void +dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout > 1) { + netif->dhcp->request_timeout--; + } + else if (netif->dhcp->request_timeout == 1) { + netif->dhcp->request_timeout--; + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this client's request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } +#if DHCP_DOES_ARP_CHECK + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } +#endif /* DHCP_DOES_ARP_CHECK */ + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } else if (dhcp->state == DHCP_REBOOTING) { + if (dhcp->tries < REBOOT_TRIES) { + dhcp_reboot(netif); + } else { + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t1_timeout(): must renew\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_RENEWING, not DHCP_BOUND */ + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || + (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("dhcp_t2_timeout(): must rebind\n")); + /* This slightly different to RFC2131: DHCPREQUEST will be sent from state + DHCP_REBINDING, not DHCP_BOUND */ + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; +#if LWIP_DNS + u8_t n; +#endif /* LWIP_DNS */ + + /* clear options we might not get from the ACK */ + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* lease time given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME); + } + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr); + +#if LWIP_DHCP_BOOTP_FILE + /* copy boot server address, + boot file name copied in dhcp_parse_reply if not overloaded */ + ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* subnet mask given? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) { + /* remember given subnet mask */ + ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK))); + dhcp->subnet_mask_given = 1; + } else { + dhcp->subnet_mask_given = 0; + } + + /* gateway router */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) { + ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER))); + } + +#if LWIP_DNS + /* DNS servers */ + for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { + ip_addr_t dns_addr; + ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n))); + dns_setserver(n, &dns_addr); + } +#endif /* LWIP_DNS */ +} + +/** Set a statically allocated struct dhcp to work with. + * Using this prevents dhcp_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct dhcp + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +dhcp_set_struct(struct netif *netif, struct dhcp *dhcp) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp != NULL", dhcp != NULL); + LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL); + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + netif->dhcp = dhcp; +} + +/** Removes a struct dhcp from a netif. + * + * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the + * struct dhcp since the memory is passed back to the heap. + * + * @param netif the netif from which to remove the struct dhcp + */ +void dhcp_cleanup(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif->dhcp != NULL) { + mem_free(netif->dhcp); + netif->dhcp = NULL; + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + */ +err_t +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + + LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* check hwtype of the netif */ + if ((netif->flags & NETIF_FLAG_ETHARP) == 0) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n")); + return ERR_ARG; + } + + /* check MTU of the netif */ + if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n")); + return ERR_MEM; + } + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n")); + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + } + LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL); + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL ); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* dhcp_set_state(&dhcp, DHCP_OFF); */ + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + return ERR_MEM; + } + ip_set_option(dhcp->pcb, SOF_BROADCAST); + /* set up local and remote port for the pcb */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* set up the recv callback and argument */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + /* Set the flag that says this netif is handled by DHCP. */ + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + */ +void +dhcp_inform(struct netif *netif) +{ + struct dhcp dhcp; + err_t result = ERR_OK; + struct udp_pcb *pcb; + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + memset(&dhcp, 0, sizeof(struct dhcp)); + dhcp_set_state(&dhcp, DHCP_INFORM); + + if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) { + /* re-use existing pcb */ + pcb = netif->dhcp->pcb; + } else { + pcb = udp_new(); + if (pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + return; + } + dhcp.pcb = pcb; + ip_set_option(dhcp.pcb, SOF_BROADCAST); + udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + } + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM); + if (result == ERR_OK) { + dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option_trailer(&dhcp); + + pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(&dhcp); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp.pcb != NULL) { + /* otherwise, the existing pcb was used */ + udp_remove(dhcp.pcb); + } +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void +dhcp_network_changed(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + if (!dhcp) + return; + switch (dhcp->state) { + case DHCP_REBINDING: + case DHCP_RENEWING: + case DHCP_BOUND: + case DHCP_REBOOTING: + netif_set_down(netif); + dhcp->tries = 0; + dhcp_reboot(netif); + break; + case DHCP_OFF: + /* stay off */ + break; + default: + dhcp->tries = 0; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + dhcp_discover(netif); + break; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param netif the network interface on which the reply was received + * @param addr The IP address we received a reply from + */ +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr) +{ + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", + ip4_addr_get_u32(addr))); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif /* DHCP_DOES_ARP_CHECK */ + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n")); + ip_addr_set_any(&dhcp->offered_ip_addr); + dhcp_set_state(dhcp, DHCP_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) { + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON; + autoip_start(netif); + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + ip_addr_t sn_mask, gw_addr; + LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t1_timeout = (u16_t)timeout; + if (dhcp->t1_timeout == 0) { + dhcp->t1_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if(timeout > 0xffff) { + timeout = 0xffff; + } + dhcp->t2_timeout = (u16_t)timeout; + if (dhcp->t2_timeout == 0) { + dhcp->t2_timeout = 1; + } + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + + /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */ + if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) { + dhcp->t1_timeout = 0; + } + + if (dhcp->subnet_mask_given) { + /* copy offered network mask */ + ip_addr_copy(sn_mask, dhcp->offered_sn_mask); + } else { + /* subnet mask not given, choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr); + if (first_octet <= 127) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); + } + } + + ip_addr_copy(gw_addr, dhcp->offered_gw_addr); + /* gateway address not given? */ + if (ip_addr_isany(&gw_addr)) { + /* copy network address */ + ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask); + /* use first host address on network as gateway */ + ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL)); + } + +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", + ip4_addr_get_u32(&dhcp->offered_ip_addr))); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", + ip4_addr_get_u32(&sn_mask))); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", + ip4_addr_get_u32(&gw_addr))); + netif_set_gw(netif, &gw_addr); + /* bring the interface up */ + netif_set_up(netif); + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif)); + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Enter REBOOTING state to verify an existing lease + * + * @param netif network interface which must reboot + */ +static err_t +dhcp_reboot(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n")); + dhcp_set_state(dhcp, DHCP_REBOOTING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr))); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* broadcast to server */ + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + if (dhcp == NULL) { + return ERR_ARG; + } + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + ip_addr_set_zero(&dhcp->server_ip_addr); + ip_addr_set_zero(&dhcp->offered_ip_addr); + ip_addr_set_zero(&dhcp->offered_sn_mask); + ip_addr_set_zero(&dhcp->offered_gw_addr); +#if LWIP_DHCP_BOOTP_FILE + ip_addr_set_zero(&dhcp->offered_si_addr); +#endif /* LWIP_DHCP_BOOTP_FILE */ + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE); + if (result == ERR_OK) { + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif); + dhcp_delete_msg(dhcp); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + /* Remove the flag that says this netif is handled by DHCP. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) { +#if LWIP_DHCP_AUTOIP_COOP + if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) { + autoip_stop(netif); + dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF; + } +#endif /* LWIP_DHCP_AUTOIP_COOP */ + + if (dhcp->pcb != NULL) { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + dhcp_set_state(dhcp, DHCP_OFF); + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + */ +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + dhcp->request_timeout = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void +dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void +dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} + +static void +dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU); +} + +static void +dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8); + dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL)); +} + +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + u8_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t +dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u8_t *options; + u16_t offset; + u16_t offset_max; + u16_t options_idx; + u16_t options_idx_max; + struct pbuf *q; + int parse_file_as_options = 0; + int parse_sname_as_options = 0; + + /* clear received options */ + dhcp_clear_all_options(dhcp); + /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */ + if (p->len < DHCP_SNAME_OFS) { + return ERR_BUF; + } + dhcp->msg_in = (struct dhcp_msg *)p->payload; +#if LWIP_DHCP_BOOTP_FILE + /* clear boot file name */ + dhcp->boot_file_name[0] = 0; +#endif /* LWIP_DHCP_BOOTP_FILE */ + + /* parse options */ + + /* start with options field */ + options_idx = DHCP_OPTIONS_OFS; + /* parse options to the end of the received packet */ + options_idx_max = p->tot_len; +again: + q = p; + while((q != NULL) && (options_idx >= q->len)) { + options_idx -= q->len; + options_idx_max -= q->len; + q = q->next; + } + if (q == NULL) { + return ERR_BUF; + } + offset = options_idx; + offset_max = options_idx_max; + options = (u8_t*)q->payload; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) { + u8_t op = options[offset]; + u8_t len; + u8_t decode_len = 0; + int decode_idx = -1; + u16_t val_offset = offset + 2; + /* len byte might be in the next pbuf */ + if (offset + 1 < q->len) { + len = options[offset + 1]; + } else { + len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0); + } + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + decode_len = len; + switch(op) { + /* case(DHCP_OPTION_END): handled above */ + case(DHCP_OPTION_PAD): + /* special option: no len encoded */ + decode_len = len = 0; + /* will be increased below */ + offset--; + break; + case(DHCP_OPTION_SUBNET_MASK): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T2; + break; + default: + decode_len = 0; + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op)); + break; + } + offset += len + 2; + if (decode_len > 0) { + u32_t value = 0; + u16_t copy_len; +decode_next: + LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX); + if (!dhcp_option_given(dhcp, decode_idx)) { + copy_len = LWIP_MIN(decode_len, 4); + pbuf_copy_partial(q, &value, copy_len, val_offset); + if (decode_len > 4) { + /* decode more than one u32_t */ + LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, htonl(value)); + decode_len -= 4; + val_offset += 4; + decode_idx++; + goto decode_next; + } else if (decode_len == 4) { + value = ntohl(value); + } else { + LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + value = ((u8_t*)&value)[0]; + } + dhcp_got_option(dhcp, decode_idx); + dhcp_set_option_value(dhcp, decode_idx, value); + } + } + if (offset >= q->len) { + offset -= q->len; + offset_max -= q->len; + if ((offset < offset_max) && offset_max) { + q = q->next; + LWIP_ASSERT("next pbuf was null", q); + options = (u8_t*)q->payload; + } else { + /* We've run out of bytes, probably no end marker. Don't proceed. */ + break; + } + } + } + /* is this an overloaded message? */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) { + u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD); + dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD); + if (overload == DHCP_OVERLOAD_FILE) { + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME) { + parse_sname_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + } else if (overload == DHCP_OVERLOAD_SNAME_FILE) { + parse_sname_as_options = 1; + parse_file_as_options = 1; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload)); + } +#if LWIP_DHCP_BOOTP_FILE + if (!parse_file_as_options) { + /* only do this for ACK messages */ + if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) && + (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK)) + /* copy bootp file name, don't care for sname (server hostname) */ + pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS); + /* make sure the string is really NULL-terminated */ + dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0; + } +#endif /* LWIP_DHCP_BOOTP_FILE */ + } + if (parse_file_as_options) { + /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */ + parse_file_as_options = 0; + options_idx = DHCP_FILE_OFS; + options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN; + goto again; + } else if (parse_sname_as_options) { + parse_sname_as_options = 0; + options_idx = DHCP_SNAME_OFS; + options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN; + goto again; + } + return ERR_OK; +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void +dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n")); + goto free_pbuf_and_return; + } + + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + goto free_pbuf_and_return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + goto free_pbuf_and_return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid)); + goto free_pbuf_and_return; + } + /* option fields could be unfold? */ + if (dhcp_parse_reply(dhcp, p) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("problem unfolding DHCP message - too short on memory?\n")); + goto free_pbuf_and_return; + } + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + goto free_pbuf_and_return; + } + + /* read DHCP message type */ + msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n")); + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } +free_pbuf_and_return: + dhcp->msg_in = NULL; + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + * @param dhcp dhcp control struct + * @param message_type message type of the request + */ +static err_t +dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type) +{ + u16_t i; +#ifndef DHCP_GLOBAL_XID + /** default global transaction identifier starting value (easy to match + * with a packet analyser). We simply increment for each new request. + * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one + * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ +#else + if (!xid_initialised) { + xid = DHCP_GLOBAL_XID; + xid_initialised = !xid_initialised; + } +#endif + LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;); + LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_create_msg(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + xid = LWIP_RAND(); +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + } + dhcp->xid = xid; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, + ("transaction id xid(%"X32_F")\n", xid)); + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + dhcp->msg_out->hlen = netif->hwaddr_len; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + /* we don't need the broadcast flag since we can receive unicast traffic + before being fully configured! */ + dhcp->msg_out->flags = 0; + ip_addr_set_zero(&dhcp->msg_out->ciaddr); + /* set ciaddr to netif->ip_addr based on message_type and state */ + if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || + ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */ + ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) { + ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr); + } + ip_addr_set_zero(&dhcp->msg_out->yiaddr); + ip_addr_set_zero(&dhcp->msg_out->siaddr); + ip_addr_set_zero(&dhcp->msg_out->giaddr); + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) { + dhcp->msg_out->sname[i] = 0; + } + for (i = 0; i < DHCP_FILE_LEN; i++) { + dhcp->msg_out->file[i] = 0; + } + dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) { + dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */ + } + /* Add option MESSAGE_TYPE */ + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, message_type); + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param dhcp the dhcp struct to free the request from + */ +static void +dhcp_delete_msg(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + if (dhcp->p_out != NULL) { + pbuf_free(dhcp->p_out); + } + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + * + * @param dhcp DHCP state structure + */ +static void +dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) && + (dhcp->options_out_len < DHCP_OPTIONS_LEN)) { + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/external/badvpn_dns/lwip/src/core/dns.c b/external/badvpn_dns/lwip/src/core/dns.c new file mode 100644 index 00000000..90821a66 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/dns.c @@ -0,0 +1,988 @@ +/** + * @file + * DNS - host name to IP address resolver. + * + */ + +/** + + * This file implements a DNS host name to IP address resolver. + + * Port to lwIP from uIP + * by Jim Pettinato April 2007 + + * uIP version Copyright (c) 2002-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DNS.C + * + * The lwIP DNS resolver functions are used to lookup a host name and + * map it to a numerical IP address. It maintains a list of resolved + * hostnames that can be queried with the dns_lookup() function. + * New hostnames can be resolved using the dns_query() function. + * + * The lwIP version of the resolver also adds a non-blocking version of + * gethostbyname() that will work with a raw API application. This function + * checks for an IP address string first and converts it if it is valid. + * gethostbyname() then does a dns_lookup() to see if the name is + * already in the table. If so, the IP is returned. If not, a query is + * issued and the function returns with a ERR_INPROGRESS status. The app + * using the dns client must then go into a waiting state. + * + * Once a hostname has been resolved (or found to be non-existent), + * the resolver code calls a specified callback function (which + * must be implemented by the module that uses the resolver). + */ + +/*----------------------------------------------------------------------------- + * RFC 1035 - Domain names - implementation and specification + * RFC 2181 - Clarifications to the DNS Specification + *----------------------------------------------------------------------------*/ + +/** @todo: define good default values (rfc compliance) */ +/** @todo: improve answer parsing, more checkings... */ +/** @todo: check RFC1035 - 7.3. Processing responses */ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */ +#endif + +/** DNS server port address */ +#ifndef DNS_SERVER_PORT +#define DNS_SERVER_PORT 53 +#endif + +/** DNS maximum number of retries when asking for a name, before "timeout". */ +#ifndef DNS_MAX_RETRIES +#define DNS_MAX_RETRIES 4 +#endif + +/** DNS resource record max. TTL (one week as default) */ +#ifndef DNS_MAX_TTL +#define DNS_MAX_TTL 604800 +#endif + +/* DNS protocol flags */ +#define DNS_FLAG1_RESPONSE 0x80 +#define DNS_FLAG1_OPCODE_STATUS 0x10 +#define DNS_FLAG1_OPCODE_INVERSE 0x08 +#define DNS_FLAG1_OPCODE_STANDARD 0x00 +#define DNS_FLAG1_AUTHORATIVE 0x04 +#define DNS_FLAG1_TRUNC 0x02 +#define DNS_FLAG1_RD 0x01 +#define DNS_FLAG2_RA 0x80 +#define DNS_FLAG2_ERR_MASK 0x0f +#define DNS_FLAG2_ERR_NONE 0x00 +#define DNS_FLAG2_ERR_NAME 0x03 + +/* DNS protocol states */ +#define DNS_STATE_UNUSED 0 +#define DNS_STATE_NEW 1 +#define DNS_STATE_ASKING 2 +#define DNS_STATE_DONE 3 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS message header */ +struct dns_hdr { + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u8_t flags1); + PACK_STRUCT_FIELD(u8_t flags2); + PACK_STRUCT_FIELD(u16_t numquestions); + PACK_STRUCT_FIELD(u16_t numanswers); + PACK_STRUCT_FIELD(u16_t numauthrr); + PACK_STRUCT_FIELD(u16_t numextrarr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_HDR 12 + +/** DNS query message structure. + No packing needed: only used locally on the stack. */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; +}; +#define SIZEOF_DNS_QUERY 4 + +/** DNS answer message structure. + No packing needed: only used locally on the stack. */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + u16_t type; + u16_t cls; + u32_t ttl; + u16_t len; +}; +#define SIZEOF_DNS_ANSWER 10 + +/** DNS table entry */ +struct dns_table_entry { + u8_t state; + u8_t numdns; + u8_t tmr; + u8_t retries; + u8_t seqno; + u8_t err; + u32_t ttl; + char name[DNS_MAX_NAME_LENGTH]; + ip_addr_t ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#if DNS_LOCAL_HOSTLIST + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Local host-list. For hostnames in this list, no + * external name resolution is performed */ +static struct local_hostlist_entry *local_hostlist_dynamic; +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE +#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ +/** Defining this allows the local_hostlist_static to be placed in a different + * linker section (e.g. FLASH) */ +#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST +#define DNS_LOCAL_HOSTLIST_STORAGE_POST +#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ +DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] + DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; + +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +static void dns_init_local(); +#endif /* DNS_LOCAL_HOSTLIST */ + + +/* forward declarations */ +static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static void dns_check_entries(void); + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcb; +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; +/** Contiguous buffer for processing responses */ +static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; +static u8_t* dns_payload; + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + ip_addr_t dnsserver; + + dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); + + /* initialize default DNS server address */ + DNS_SERVER_ADDRESS(&dnsserver); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ + if (dns_pcb == NULL) { + dns_pcb = udp_new(); + + if (dns_pcb != NULL) { + /* initialize DNS table not needed (initialized to zero since it is a + * global variable) */ + LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", + DNS_STATE_UNUSED == 0); + + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); + udp_recv(dns_pcb, dns_recv, NULL); + + /* initialize default DNS primary server */ + dns_setserver(0, &dnsserver); + } + } +#if DNS_LOCAL_HOSTLIST + dns_init_local(); +#endif +} + +/** + * Initialize one of the DNS servers. + * + * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS + * @param dnsserver IP address of the DNS server to set + */ +void +dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { + dns_servers[numdns] = (*dnsserver); + } +} + +/** + * Obtain one of the currently configured DNS server. + * + * @param numdns the index of the DNS server + * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS + * server has not been configured. + */ +ip_addr_t +dns_getserver(u8_t numdns) +{ + if (numdns < DNS_MAX_SERVERS) { + return dns_servers[numdns]; + } else { + return *IP_ADDR_ANY; + } +} + +/** + * The DNS resolver client timer - handle retries and timeouts and should + * be called every DNS_TMR_INTERVAL milliseconds (every second by default). + */ +void +dns_tmr(void) +{ + if (dns_pcb != NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); + } +} + +#if DNS_LOCAL_HOSTLIST +static void +dns_init_local() +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) + int i; + struct local_hostlist_entry *entry; + /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ + struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; + size_t namelen; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); + namelen = strlen(init_entry->name); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, init_entry->name, namelen); + ((char*)entry->name)[namelen] = 0; + entry->addr = init_entry->addr; + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ +} + +/** + * Scans the local host-list for a hostname. + * + * @param hostname Hostname to look for in the local host-list + * @return The first IP address for the hostname in the local host-list or + * IPADDR_NONE if not found. + */ +static u32_t +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if(strcmp(entry->name, hostname) == 0) { + return ip4_addr_get_u32(&entry->addr); + } + entry = entry->next; + } +#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + int i; + for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { + if(strcmp(local_hostlist_static[i].name, hostname) == 0) { + return ip4_addr_get_u32(&local_hostlist_static[i].addr); + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return IPADDR_NONE; +} + +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +/** Remove all entries from the local host-list for a specific hostname + * and/or IP addess + * + * @param hostname hostname for which entries shall be removed from the local + * host-list + * @param addr address for which entries shall be removed from the local host-list + * @return the number of removed entries + */ +int +dns_local_removehost(const char *hostname, const ip_addr_t *addr) +{ + int removed = 0; + struct local_hostlist_entry *entry = local_hostlist_dynamic; + struct local_hostlist_entry *last_entry = NULL; + while (entry != NULL) { + if (((hostname == NULL) || !strcmp(entry->name, hostname)) && + ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { + struct local_hostlist_entry *free_entry; + if (last_entry != NULL) { + last_entry->next = entry->next; + } else { + local_hostlist_dynamic = entry->next; + } + free_entry = entry; + entry = entry->next; + memp_free(MEMP_LOCALHOSTLIST, free_entry); + removed++; + } else { + last_entry = entry; + entry = entry->next; + } + } + return removed; +} + +/** + * Add a hostname/IP address pair to the local host-list. + * Duplicates are not checked. + * + * @param hostname hostname of the new entry + * @param addr IP address of the new entry + * @return ERR_OK if succeeded or ERR_MEM on memory error + */ +err_t +dns_local_addhost(const char *hostname, const ip_addr_t *addr) +{ + struct local_hostlist_entry *entry; + size_t namelen; + LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); + namelen = strlen(hostname); + LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); + entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = (char*)entry + sizeof(struct local_hostlist_entry); + MEMCPY((char*)entry->name, hostname, namelen); + ((char*)entry->name)[namelen] = 0; + ip_addr_copy(entry->addr, *addr); + entry->next = local_hostlist_dynamic; + local_hostlist_dynamic = entry; + return ERR_OK; +} +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** + * Look up a hostname in the array of known hostnames. + * + * @note This function only looks in the internal array of known + * hostnames, it does not send out a query for the hostname if none + * was found. The function dns_enqueue() can be used to send a query + * for a hostname. + * + * @param name the hostname to look up + * @return the hostname's IP address, as u32_t (instead of ip_addr_t to + * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +static u32_t +dns_lookup(const char *name) +{ + u8_t i; +#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) + u32_t addr; +#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ +#if DNS_LOCAL_HOSTLIST + if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { + return addr; + } +#endif /* DNS_LOOKUP_LOCAL_EXTERN */ + + /* Walk through name list, return entry if found. If not, return NULL. */ + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + if ((dns_table[i].state == DNS_STATE_DONE) && + (strcmp(name, dns_table[i].name) == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); + ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + return ip4_addr_get_u32(&dns_table[i].ipaddr); + } + } + + return IPADDR_NONE; +} + +#if DNS_DOES_NAME_CHECK +/** + * Compare the "dotted" name "query" with the encoded name "response" + * to make sure an answer from the DNS server matches the current dns_table + * entry (otherwise, answers might arrive late for hostname not on the list + * any more). + * + * @param query hostname (not encoded) from the dns_table + * @param response encoded hostname in the DNS response + * @return 0: names equal; 1: names differ + */ +static u8_t +dns_compare_name(unsigned char *query, unsigned char *response) +{ + unsigned char n; + + do { + n = *response++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != (*response)) { + return 1; + } + ++response; + ++query; + --n; + }; + ++query; + } + } while (*response != 0); + + return 0; +} +#endif /* DNS_DOES_NAME_CHECK */ + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param query encoded DNS name in the DNS server response + * @return end of the name + */ +static unsigned char * +dns_parse_name(unsigned char *query) +{ + unsigned char n; + + do { + n = *query++; + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query; + --n; + }; + } + } while (*query != 0); + + return query + 1; +} + +/** + * Send a DNS query packet. + * + * @param numdns index of the DNS server in the dns_servers table + * @param name hostname to query + * @param id index of the hostname in dns_table, used as transaction ID in the + * DNS query packet + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(u8_t numdns, const char* name, u8_t id) +{ + err_t err; + struct dns_hdr *hdr; + struct dns_query qry; + struct pbuf *p; + char *query, *nptr; + const char *pHostname; + u8_t n; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(numdns), name)); + LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); + + /* if here, we have either a new query or a retry on a previous query to process */ + p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 1 + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + u16_t realloc_size; + LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); + /* fill dns header */ + hdr = (struct dns_hdr*)p->payload; + memset(hdr, 0, SIZEOF_DNS_HDR); + hdr->id = htons(id); + hdr->flags1 = DNS_FLAG1_RD; + hdr->numquestions = PP_HTONS(1); + query = (char*)hdr + SIZEOF_DNS_HDR; + pHostname = name; + --pHostname; + + /* convert hostname into suitable query format. */ + do { + ++pHostname; + nptr = query; + ++query; + for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { + *query = *pHostname; + ++query; + ++n; + } + *nptr = n; + } while(*pHostname != 0); + *query++='\0'; + + /* fill dns query */ + qry.type = PP_HTONS(DNS_RRTYPE_A); + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + realloc_size = (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))); + LWIP_ASSERT("p->tot_len >= realloc_size", p->tot_len >= realloc_size); + pbuf_realloc(p, realloc_size); + + /* connect to the server for faster receiving */ + udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); + /* send dns packet */ + err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +/** + * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. + * Check an entry in the dns_table: + * - send out query for new entries + * - retry old pending entries on timeout (also with different servers) + * - remove completed entries from the table if their TTL has expired + * + * @param i index of the dns_table entry to check + */ +static void +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *pEntry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch(pEntry->state) { + + case DNS_STATE_NEW: { + /* initialize new entry */ + pEntry->state = DNS_STATE_ASKING; + pEntry->numdns = 0; + pEntry->tmr = 1; + pEntry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1])) { + /* change of server */ + pEntry->numdns++; + pEntry->tmr = 1; + pEntry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); + /* call specified callback function if provided */ + if (pEntry->found) + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + break; + } + } + + /* wait longer for the next retry */ + pEntry->tmr = pEntry->retries; + + /* send DNS packet for this entry */ + err = dns_send(pEntry->numdns, pEntry->name, i); + if (err != ERR_OK) { + LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("dns_send returned error: %s\n", lwip_strerr(err))); + } + } + break; + } + + case DNS_STATE_DONE: { + /* if the time to live is nul */ + if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + } + break; + } + case DNS_STATE_UNUSED: + /* nothing to do */ + break; + default: + LWIP_ASSERT("unknown dns_table entry state:", 0); + break; + } +} + +/** + * Call dns_check_entry for each entry in dns_table - check all entries. + */ +static void +dns_check_entries(void) +{ + u8_t i; + + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + dns_check_entry(i); + } +} + +/** + * Receive input function for DNS response packets arriving for the dns UDP pcb. + * + * @params see udp.h + */ +static void +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u16_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u16_t nquestions, nanswers; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(port); + + /* is the dns message too big ? */ + if (p->tot_len > DNS_MSG_SIZE) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); + /* free pbuf and return */ + goto memerr; + } + + /* is the dns message big enough ? */ + if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); + /* free pbuf and return */ + goto memerr; + } + + /* copy dns payload inside static buffer for processing */ + if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { + /* The ID in the DNS header should be our entry into the name table. */ + hdr = (struct dns_hdr*)dns_payload; + i = htons(hdr->id); + if (i < DNS_TABLE_SIZE) { + pEntry = &dns_table[i]; + if(pEntry->state == DNS_STATE_ASKING) { + /* This entry is now completed. */ + pEntry->state = DNS_STATE_DONE; + pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + + /* We only care about the question(s) and the answers. The authrr + and the extrarr are simply discarded. */ + nquestions = htons(hdr->numquestions); + nanswers = htons(hdr->numanswers); + + /* Check for error. If so, call callback to inform. */ + if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + +#if DNS_DOES_NAME_CHECK + /* Check if the name in the "question" part match with the name in the entry. */ + if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } +#endif /* DNS_DOES_NAME_CHECK */ + + /* Skip the name in the "question" part */ + pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; + + while (nanswers > 0) { + /* skip answer resource record's host name */ + pHostname = (char *) dns_parse_name((unsigned char *)pHostname); + + /* Check for IP address type and Internet class. Others are discarded. */ + SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && + (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) { + /* read the answer resource record's TTL, and maximize it if needed */ + pEntry->ttl = ntohl(ans.ttl); + if (pEntry->ttl > DNS_MAX_TTL) { + pEntry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t)); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); + } + if (pEntry->ttl == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + goto flushentry; + } + /* deallocate memory and return */ + goto memerr; + } else { + pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + } + } + + /* deallocate memory and return */ + goto memerr; + +responseerr: + /* ERROR: call specified callback function with NULL as name to indicate an error */ + if (pEntry->found) { + (*pEntry->found)(pEntry->name, NULL, pEntry->arg); + } +flushentry: + /* flush this entry */ + pEntry->state = DNS_STATE_UNUSED; + pEntry->found = NULL; + +memerr: + /* free pbuf */ + pbuf_free(p); + return; +} + +/** + * Queues a new hostname to resolve and sends out a DNS query for that hostname + * + * @param name the hostname that is to be queried + * @param hostnamelen length of the hostname + * @param found a callback founction to be called on success, failure or timeout + * @param callback_arg argument to pass to the callback function + * @return @return a err_t return code. + */ +static err_t +dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, + void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *pEntry = NULL; + size_t namelen; + + /* search an unused entry, or the oldest one */ + lseq = lseqi = 0; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + pEntry = &dns_table[i]; + /* is it an unused entry ? */ + if (pEntry->state == DNS_STATE_UNUSED) + break; + + /* check if this is the oldest completed entry */ + if (pEntry->state == DNS_STATE_DONE) { + if ((dns_seqno - pEntry->seqno) > lseq) { + lseq = dns_seqno - pEntry->seqno; + lseqi = i; + } + } + } + + /* if we don't have found an unused entry, use the oldest completed one */ + if (i == DNS_TABLE_SIZE) { + if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { + /* no entry can't be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); + return ERR_MEM; + } else { + /* use the oldest completed one */ + i = lseqi; + pEntry = &dns_table[i]; + } + } + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + pEntry->state = DNS_STATE_NEW; + pEntry->seqno = dns_seqno++; + pEntry->found = found; + pEntry->arg = callback_arg; + namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); + MEMCPY(pEntry->name, name, namelen); + pEntry->name[namelen] = 0; + + /* force to send query without waiting timer */ + dns_check_entry(i); + + /* dns query is enqueued */ + return ERR_INPROGRESS; +} + +/** + * Resolve a hostname (string) into an IP address. + * NON-BLOCKING callback version for use with raw API!!! + * + * Returns immediately with one of err_t return codes: + * - ERR_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ERR_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ERR_ARG: dns client not initialized or invalid hostname + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a ip_addr_t where to store the address if it is already + * cached in the dns_table (only valid if ERR_OK is returned!) + * @param found a callback function to be called on success, failure or timeout (only if + * ERR_INPROGRESS is returned!) + * @param callback_arg argument to pass to the callback function + * @return a err_t return code. + */ +err_t +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + u32_t ipaddr; + size_t hostnamelen; + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((dns_pcb == NULL) || (addr == NULL) || + (!hostname) || (!hostname[0])) { + return ERR_ARG; + } + hostnamelen = strlen(hostname); + if (hostnamelen >= DNS_MAX_NAME_LENGTH) { + return ERR_ARG; + } + + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname, "localhost")==0) { + ip_addr_set_loopback(addr); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK */ + ipaddr = ipaddr_addr(hostname); + if (ipaddr == IPADDR_NONE) { + /* already have this address cached? */ + ipaddr = dns_lookup(hostname); + } + if (ipaddr != IPADDR_NONE) { + ip4_addr_set_u32(addr, ipaddr); + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, hostnamelen, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/external/badvpn_dns/lwip/src/core/inet_chksum.c b/external/badvpn_dns/lwip/src/core/inet_chksum.c new file mode 100644 index 00000000..8bc42c14 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/inet_chksum.c @@ -0,0 +1,545 @@ +/** + * @file + * Incluse internet checksum functions. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/inet_chksum.h" +#include "lwip/def.h" + +#include +#include + +/* These are some reference implementations of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. If you create + * your own version, link it in and in your cc.h put: + * + * #define LWIP_CHKSUM + * + * Or you can select from the implementations below by defining + * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3. + */ + +#ifndef LWIP_CHKSUM +# define LWIP_CHKSUM lwip_standard_chksum +# ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 2 +# endif +u16_t lwip_standard_chksum(void *dataptr, int len); +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */ +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable length to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000UL) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */ +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((mem_ptr_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)(void *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) { + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + /* Swap if alignment was odd */ + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */ +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +u16_t +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = (u8_t *)dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((mem_ptr_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((mem_ptr_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) { + tmp++; /* add back carry */ + } + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) { + sum++; /* add back carry */ + } + + len -= 8; + } + + /* make room in upper bits */ + sum = FOLD_U32T(sum); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) { /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + } + + sum += t; /* add end bytes */ + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + sum = FOLD_U32T(sum); + sum = FOLD_U32T(sum); + + if (odd) { + sum = SWAP_BYTES_IN_WORD(sum); + } + + return (u16_t)sum; +} +#endif + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* just executing this next line is probably faster that the if statement needed + to check whether we really need to execute it, and does no harm */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + u16_t chklen; + + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + /* fold the upper bit down */ + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is propably faster than if statements... */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum_pseudo_partial: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + addr = ip4_addr_get_u32(src); + acc = (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = ip4_addr_get_u32(dest); + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarily for IP + * and ICMP. + * + * @param dataptr start of the buffer to calculate the checksum (no alignment needed) + * @param len length of the buffer to calculate the checksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + return ~LWIP_CHKSUM(dataptr, len); +} + +/** + * Calculate a checksum over a chain of pbufs (without pseudo-header, much like + * inet_chksum only pbufs are used). + * + * @param p pbuf chain over that the checksum should be calculated + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + acc = FOLD_U32T(acc); + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = SWAP_BYTES_IN_WORD(acc); + } + } + + if (swapped) { + acc = SWAP_BYTES_IN_WORD(acc); + } + return (u16_t)~(acc & 0xffffUL); +} + +/* These are some implementations for LWIP_CHKSUM_COPY, which copies data + * like MEMCPY but generates a checksum at the same time. Since this is a + * performance-sensitive function, you might want to create your own version + * in assembly targeted at your hardware by defining it in lwipopts.h: + * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len) + */ + +#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */ +/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM. + * For architectures with big caches, data might still be in cache when + * generating the checksum after copying. + */ +u16_t +lwip_chksum_copy(void *dst, const void *src, u16_t len) +{ + MEMCPY(dst, src, len); + return LWIP_CHKSUM(dst, len); +} +#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */ diff --git a/external/badvpn_dns/lwip/src/core/init.c b/external/badvpn_dns/lwip/src/core/init.c new file mode 100644 index 00000000..c24c0274 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/init.c @@ -0,0 +1,345 @@ +/** + * @file + * Modules initialization + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/init.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/sockets.h" +#include "lwip/ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/timers.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" + +/* Compile-time sanity checks for configuration errors. + * These can be done independently of LWIP_DEBUG, without penalty. + */ +#ifndef BYTE_ORDER + #error "BYTE_ORDER is not defined, you have to define it in your cc.h" +#endif +#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV) + #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_UDPLITE) + #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DHCP) + #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_IGMP) + #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_SNMP) + #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (!LWIP_UDP && LWIP_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0)) + #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h" +#endif +#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0)) + #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0)) + #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0)) + #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h" +#endif +#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1)) + #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h" +#endif +#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) + #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" +#endif +/* There must be sufficient timeouts, taking into account requirements of the subsystems. */ +#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts" +#endif +#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS)) + #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!" +#endif +#endif /* !MEMP_MEM_MALLOC */ +#if (LWIP_TCP && (TCP_WND > 0xffff)) + #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff)) + #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2)) + #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work" +#endif +#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12))) + #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h" +#endif +#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)) + #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t" +#endif +#if (LWIP_NETIF_API && (NO_SYS==1)) + #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1)) + #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h" +#endif +#if (!LWIP_NETCONN && LWIP_SOCKET) + #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP) + #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h" +#endif +#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK) + #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h" +#endif +#if (!LWIP_ARP && LWIP_AUTOIP) + #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0)) + #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h" +#endif +#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) + #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" +#endif +#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API))) + #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h" +#endif +#if (MEM_LIBC_MALLOC && MEM_USE_POOLS) + #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h" +#endif +#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS) + #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h" +#endif +#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT) + #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf" +#endif +#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT))) + #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST" +#endif +#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT + #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on" +#endif +#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT) + #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT" +#endif +#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND) + #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value" +#endif +#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING + #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too" +#endif +#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE + #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets" +#endif +#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF + #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues" +#endif +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_ACCEPTCONN != SOF_ACCEPTCONN + #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" +#endif +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#if SO_LINGER != SOF_LINGER + #error "WARNING: SO_LINGER != SOF_LINGER" +#endif +#endif /* LWIP_SOCKET */ + + +/* Compile-time checks for deprecated options. + */ +#ifdef MEMP_NUM_TCPIP_MSG + #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef MEMP_NUM_API_MSG + #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef TCP_REXMIT_DEBUG + #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef RAW_STATS + #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_QUEUE_FIRST + #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h." +#endif +#ifdef ETHARP_ALWAYS_INSERT + #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h." +#endif + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ +#endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); +#if LWIP_SOCKET + lwip_socket_init(); +#endif /* LWIP_SOCKET */ + ip_init(); +#if LWIP_ARP + etharp_init(); +#endif /* LWIP_ARP */ +#if LWIP_RAW + raw_init(); +#endif /* LWIP_RAW */ +#if LWIP_UDP + udp_init(); +#endif /* LWIP_UDP */ +#if LWIP_TCP + tcp_init(); +#endif /* LWIP_TCP */ +#if LWIP_SNMP + snmp_init(); +#endif /* LWIP_SNMP */ +#if LWIP_AUTOIP + autoip_init(); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + igmp_init(); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + dns_init(); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + ip6_init(); + nd6_init(); +#if LWIP_IPV6_MLD + mld6_init(); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/external/badvpn_dns/lwip/src/core/ipv4/autoip.c b/external/badvpn_dns/lwip/src/core/ipv4/autoip.c new file mode 100644 index 00000000..b122da27 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/autoip.c @@ -0,0 +1,528 @@ +/** + * @file + * AutoIP Automatic LinkLocal IP Configuration + * + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +/******************************************************************************* + * USAGE: + * + * define LWIP_AUTOIP 1 in your lwipopts.h + * + * If you don't use tcpip.c (so, don't call, you don't call tcpip_init): + * - First, call autoip_init(). + * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces, + * that should be defined in autoip.h. + * I recommend a value of 100. The value must divide 1000 with a remainder almost 0. + * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 .... + * + * Without DHCP: + * - Call autoip_start() after netif_add(). + * + * With DHCP: + * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h. + * - Configure your DHCP Client. + * + */ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" + +#include +#include + +/* 169.254.0.0 */ +#define AUTOIP_NET 0xA9FE0000 +/* 169.254.1.0 */ +#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100) +/* 169.254.254.255 */ +#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF) + + +/** Pseudo random macro based on netif informations. + * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */ +#ifndef LWIP_AUTOIP_RAND +#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \ + ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \ + ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \ + ((u32_t)((netif->hwaddr[4]) & 0xff))) + \ + (netif->autoip?netif->autoip->tried_llipaddr:0)) +#endif /* LWIP_AUTOIP_RAND */ + +/** + * Macro that generates the initial IP address to be tried by AUTOIP. + * If you want to override this, define it to something else in lwipopts.h. + */ +#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR +#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \ + htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \ + ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8))) +#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */ + +/* static functions */ +static void autoip_handle_arp_conflict(struct netif *netif); + +/* creates a pseudo random LL IP-Address for a network interface */ +static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr); + +/* sends an ARP probe */ +static err_t autoip_arp_probe(struct netif *netif); + +/* sends an ARP announce */ +static err_t autoip_arp_announce(struct netif *netif); + +/* configure interface for use with current LL IP-Address */ +static err_t autoip_bind(struct netif *netif); + +/* start sending probes for llipaddr */ +static void autoip_start_probing(struct netif *netif); + + +/** Set a statically allocated struct autoip to work with. + * Using this prevents autoip_start to allocate it using mem_malloc. + * + * @param netif the netif for which to set the struct autoip + * @param dhcp (uninitialised) dhcp struct allocated by the application + */ +void +autoip_set_struct(struct netif *netif, struct autoip *autoip) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("autoip != NULL", autoip != NULL); + LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL); + + /* clear data structure */ + memset(autoip, 0, sizeof(struct autoip)); + /* autoip->state = AUTOIP_STATE_OFF; */ + netif->autoip = autoip; +} + +/** Restart AutoIP client and check the next address (conflict detected) + * + * @param netif The netif under AutoIP control + */ +static void +autoip_restart(struct netif *netif) +{ + netif->autoip->tried_llipaddr++; + autoip_start(netif); +} + +/** + * Handle a IP address conflict after an ARP conflict detection + */ +static void +autoip_handle_arp_conflict(struct netif *netif) +{ + /* Somehow detect if we are defending or retreating */ + unsigned char defend = 1; /* tbd */ + + if (defend) { + if (netif->autoip->lastconflict > 0) { + /* retreat, there was a conflicting ARP in the last + * DEFEND_INTERVAL seconds + */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n")); + + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n")); + autoip_arp_announce(netif); + netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } + } else { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_handle_arp_conflict(): we do not defend, retreating\n")); + /* TODO: close all TCP sessions */ + autoip_restart(netif); + } +} + +/** + * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * + * @param netif network interface on which create the IP-Address + * @param ipaddr ip address to initialize + */ +static void +autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255 + * compliant to RFC 3927 Section 2.1 + * We have 254 * 256 possibilities */ + + u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif)); + addr += netif->autoip->tried_llipaddr; + addr = AUTOIP_NET | (addr & 0xffff); + /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */ + + if (addr < AUTOIP_RANGE_START) { + addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + if (addr > AUTOIP_RANGE_END) { + addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1; + } + LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) && + (addr <= AUTOIP_RANGE_END)); + ip4_addr_set_u32(ipaddr, htonl(addr)); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), + ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); +} + +/** + * Sends an ARP probe from a network interface + * + * @param netif network interface used to send the probe + */ +static err_t +autoip_arp_probe(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Sends an ARP announce from a network interface + * + * @param netif network interface used to send the announce + */ +static err_t +autoip_arp_announce(struct netif *netif) +{ + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, ðzero, + &netif->autoip->llipaddr, ARP_REQUEST); +} + +/** + * Configure interface for use with current LL IP-Address + * + * @param netif network interface to configure with current LL IP-Address + */ +static err_t +autoip_bind(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + ip_addr_t sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, + ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr), + ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr))); + + IP4_ADDR(&sn_mask, 255, 255, 0, 0); + IP4_ADDR(&gw_addr, 0, 0, 0, 0); + + netif_set_ipaddr(netif, &autoip->llipaddr); + netif_set_netmask(netif, &sn_mask); + netif_set_gw(netif, &gw_addr); + + /* bring the interface up */ + netif_set_up(netif); + + return ERR_OK; +} + +/** + * Start AutoIP client + * + * @param netif network interface on which start the AutoIP client + */ +err_t +autoip_start(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + err_t result = ERR_OK; + + if (netif_is_up(netif)) { + netif_set_down(netif); + } + + /* Set IP-Address, Netmask and Gateway to 0 to make sure that + * ARP Packets are formed correctly + */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], + netif->name[1], (u16_t)netif->num)); + if (autoip == NULL) { + /* no AutoIP client attached yet? */ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): starting new AUTOIP client\n")); + autoip = (struct autoip *)mem_malloc(sizeof(struct autoip)); + if (autoip == NULL) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_start(): could not allocate autoip\n")); + return ERR_MEM; + } + memset(autoip, 0, sizeof(struct autoip)); + /* store this AutoIP client in the netif */ + netif->autoip = autoip; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip")); + } else { + autoip->state = AUTOIP_STATE_OFF; + autoip->ttw = 0; + autoip->sent_num = 0; + ip_addr_set_zero(&autoip->llipaddr); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip_start_probing(netif); + + return result; +} + +static void +autoip_start_probing(struct netif *netif) +{ + struct autoip *autoip = netif->autoip; + + autoip->state = AUTOIP_STATE_PROBING; + autoip->sent_num = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + + /* time to wait to first probe, this is randomly + * choosen out of 0 to PROBE_WAIT seconds. + * compliant to RFC 3927 Section 2.2.1 + */ + autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND)); + + /* + * if we tried more then MAX_CONFLICTS we must limit our rate for + * accquiring and probing address + * compliant to RFC 3927 Section 2.2.1 + */ + if (autoip->tried_llipaddr > MAX_CONFLICTS) { + autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND; + } +} + +/** + * Handle a possible change in the network configuration. + * + * If there is an AutoIP address configured, take the interface down + * and begin probing with the same address. + */ +void +autoip_network_changed(struct netif *netif) +{ + if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) { + netif_set_down(netif); + autoip_start_probing(netif); + } +} + +/** + * Stop AutoIP client + * + * @param netif network interface on which stop the AutoIP client + */ +err_t +autoip_stop(struct netif *netif) +{ + netif->autoip->state = AUTOIP_STATE_OFF; + netif_set_down(netif); + return ERR_OK; +} + +/** + * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds + */ +void +autoip_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on AutoIP configured interfaces */ + if (netif->autoip != NULL) { + if (netif->autoip->lastconflict > 0) { + netif->autoip->lastconflict--; + } + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n", + (u16_t)(netif->autoip->state), netif->autoip->ttw)); + + switch(netif->autoip->state) { + case AUTOIP_STATE_PROBING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num >= PROBE_NUM) { + netif->autoip->state = AUTOIP_STATE_ANNOUNCING; + netif->autoip->sent_num = 0; + netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } else { + autoip_arp_probe(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() PROBING Sent Probe\n")); + netif->autoip->sent_num++; + /* calculate time to wait to next probe */ + netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) % + ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) + + PROBE_MIN * AUTOIP_TICKS_PER_SECOND); + } + } + break; + + case AUTOIP_STATE_ANNOUNCING: + if (netif->autoip->ttw > 0) { + netif->autoip->ttw--; + } else { + if (netif->autoip->sent_num == 0) { + /* We are here the first time, so we waited ANNOUNCE_WAIT seconds + * Now we can bind to an IP address and use it. + * + * autoip_bind calls netif_set_up. This triggers a gratuitous ARP + * which counts as an announcement. + */ + autoip_bind(netif); + } else { + autoip_arp_announce(netif); + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_tmr() ANNOUNCING Sent Announce\n")); + } + netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND; + netif->autoip->sent_num++; + + if (netif->autoip->sent_num >= ANNOUNCE_NUM) { + netif->autoip->state = AUTOIP_STATE_BOUND; + netif->autoip->sent_num = 0; + netif->autoip->ttw = 0; + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr), + ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr))); + } + } + break; + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * Handles every incoming ARP Packet, called by etharp_arp_input. + * + * @param netif network interface to use for autoip processing + * @param hdr Incoming ARP packet + */ +void +autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr) +{ + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n")); + if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) { + /* when ip.src == llipaddr && hw.src != netif->hwaddr + * + * when probing ip.dst == llipaddr && hw.src != netif->hwaddr + * we have a conflict and must solve it + */ + ip_addr_t sipaddr, dipaddr; + struct eth_addr netifaddr; + ETHADDR16_COPY(netifaddr.addr, netif->hwaddr); + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + if ((netif->autoip->state == AUTOIP_STATE_PROBING) || + ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) && + (netif->autoip->sent_num == 0))) { + /* RFC 3927 Section 2.2.1: + * from beginning to after ANNOUNCE_WAIT + * seconds we have a conflict if + * ip.src == llipaddr OR + * ip.dst == llipaddr && hw.src != own hwaddr + */ + if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) || + (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Probe Conflict detected\n")); + autoip_restart(netif); + } + } else { + /* RFC 3927 Section 2.5: + * in any state we have a conflict if + * ip.src == llipaddr && hw.src != own hwaddr + */ + if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) && + !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) { + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING, + ("autoip_arp_reply(): Conflicting ARP-Packet detected\n")); + autoip_handle_arp_conflict(netif); + } + } + } +} + +#endif /* LWIP_AUTOIP */ diff --git a/external/badvpn_dns/lwip/src/core/ipv4/icmp.c b/external/badvpn_dns/lwip/src/core/ipv4/icmp.c new file mode 100644 index 00000000..af471533 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/icmp.c @@ -0,0 +1,338 @@ +/** + * @file + * ICMP - Internet Control Message Protocol + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include "lwip/opt.h" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be + * used to modify and send a response packet (and to 1 if this is not the case, + * e.g. when link header is stripped of when receiving) */ +#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1 +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + +/* The amount of data from the original packet to return in a dest-unreachable */ +#define ICMP_DEST_UNREACH_DATASIZE 8 + +static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code); + +/** + * Processes ICMP input packets, called from ip_input(). + * + * Currently only processes icmp echo requests and sends + * out the echo response. + * + * @param p the icmp echo request packet, p->payload pointing to the icmp header + * @param inp the netif on which this packet was received + */ +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; +#ifdef LWIP_DEBUG + u8_t code; +#endif /* LWIP_DEBUG */ + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + iphdr = (struct ip_hdr *)ip_current_header(); + hlen = IPH_HL(iphdr) * 4; + if (p->len < sizeof(u16_t)*2) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + goto lenerr; + } + + type = *((u8_t *)p->payload); +#ifdef LWIP_DEBUG + code = *(((u8_t *)p->payload)+1); +#endif /* LWIP_DEBUG */ + switch (type) { + case ICMP_ER: + /* This is OK, echo reply might have been parsed by a raw PCB + (as obviously, an echo request has been sent, too). */ + break; + case ICMP_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(ip_current_dest_addr())) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not acceptd? */ + if (!accepted) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + } +#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */ + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + goto lenerr; + } + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + /* p is not big enough to contain link headers + * allocate a new one and copy p into it + */ + struct pbuf *r; + /* switch p->payload to ip header */ + if (pbuf_header(p, hlen)) { + LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); + goto memerr; + } + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", + (r->len >= hlen + sizeof(struct icmp_echo_hdr))); + /* copy the whole packet including ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); + goto memerr; + } + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + /* free the original p */ + pbuf_free(p); + /* we now have an identical copy of p that has room for link headers */ + p = r; + } else { + /* restore p->payload to point to icmp header */ + if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) { + LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + goto memerr; + } + } +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ + /* At this point, all checks are OK. */ + /* We generate an answer by switching the dest and src ip addresses, + * setting the icmp type to ECHO_RESPONSE and updating the checksum. */ + iecho = (struct icmp_echo_hdr *)p->payload; + ip_addr_copy(iphdr->src, *ip_current_dest_addr()); + ip_addr_copy(iphdr->dest, *ip_current_src_addr()); + ICMPH_TYPE_SET(iecho, ICMP_ER); +#if CHECKSUM_GEN_ICMP + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* Set the correct TTL and recalculate the header checksum. */ + IPH_TTL_SET(iphdr, ICMP_TTL); + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + if(pbuf_header(p, hlen)) { + LWIP_ASSERT("Can't move over header in packet", 0); + } else { + err_t ret; + /* send an ICMP packet, src addr is the dest addr of the curren packet */ + ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL, + ICMP_TTL, 0, IP_PROTO_ICMP, inp); + if (ret != ERR_OK) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret)); + } + } + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", + (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); + return; +lenerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; +#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN +memerr: + pbuf_free(p); + ICMP_STATS_INC(icmp.err); + snmp_inc_icmpinerrors(); + return; +#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */ +} + +/** + * Send an icmp 'destination unreachable' packet, called from ip_input() if + * the transport layer protocol is unknown and from udp_input() if the local + * port is not bound. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'unreachable' packet + */ +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + icmp_send_response(p, ICMP_DUR, t); +} + +#if IP_FORWARD || IP_REASSEMBLY +/** + * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IP header + * @param t type of the 'time exceeded' packet + */ +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + icmp_send_response(p, ICMP_TE, t); +} + +#endif /* IP_FORWARD || IP_REASSEMBLY */ + +/** + * Send an icmp packet in response to an incoming packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IP header + * @param type Type of the ICMP header + * @param code Code of the ICMP header + */ +static void +icmp_send_response(struct pbuf *p, u8_t type, u8_t code) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + /* we can use the echo header here */ + struct icmp_echo_hdr *icmphdr; + ip_addr_t iphdr_src; + + /* ICMP header + IP header + 8 bytes of data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE))); + + iphdr = (struct ip_hdr *)p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + icmphdr = (struct icmp_echo_hdr *)q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload, + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE); + + /* calculate checksum */ + icmphdr->chksum = 0; + icmphdr->chksum = inet_chksum(icmphdr, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_addr_copy(iphdr_src, iphdr->src); + ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/external/badvpn_dns/lwip/src/core/ipv4/igmp.c b/external/badvpn_dns/lwip/src/core/ipv4/igmp.c new file mode 100644 index 00000000..bd52744f --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/igmp.c @@ -0,0 +1,805 @@ +/** + * @file + * IGMP - Internet Group Management Protocol + * + */ + +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +/*------------------------------------------------------------- +Note 1) +Although the rfc requires V1 AND V2 capability +we will only support v2 since now V1 is very old (August 1989) +V1 can be added if required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 2) +A query for a specific group address (as opposed to ALLHOSTS) +has now been implemented as I am unsure if it is required + +a debug print and statistic have been implemented to +show this up. +------------------------------------------------------------- +------------------------------------------------------------- +Note 3) +The router alert rfc 2113 is implemented in outgoing packets +but not checked rigorously incoming +------------------------------------------------------------- +Steve Reynolds +------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * RFC 988 - Host extensions for IP multicasting - V0 + * RFC 1054 - Host extensions for IP multicasting - + * RFC 1112 - Host extensions for IP multicasting - V1 + * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard) + * RFC 3376 - Internet Group Management Protocol, Version 3 - V3 + * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+ + * RFC 2113 - IP Router Alert Option - + *----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + * Includes + *----------------------------------------------------------------------------*/ + +#include "lwip/opt.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/igmp.h" +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#include "string.h" + +/* + * IGMP constants + */ +#define IGMP_TTL 1 +#define IGMP_MINLEN 8 +#define ROUTER_ALERT 0x9404U +#define ROUTER_ALERTLEN 4 + +/* + * IGMP message types, including version number. + */ +#define IGMP_MEMB_QUERY 0x11 /* Membership query */ +#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ +#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ +#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ + +/* Group membership states */ +#define IGMP_GROUP_NON_MEMBER 0 +#define IGMP_GROUP_DELAYING_MEMBER 1 +#define IGMP_GROUP_IDLE_MEMBER 2 + +/** + * IGMP packet format. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct igmp_msg { + PACK_STRUCT_FIELD(u8_t igmp_msgtype); + PACK_STRUCT_FIELD(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr); +static err_t igmp_remove_group(struct igmp_group *group); +static void igmp_timeout( struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); +static void igmp_send(struct igmp_group *group, u8_t type); + + +static struct igmp_group* igmp_group_list; +static ip_addr_t allsystems; +static ip_addr_t allrouters; + + +/** + * Initialize the IGMP module + */ +void +igmp_init(void) +{ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n")); + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); +} + +#ifdef LWIP_DEBUG +/** + * Dump global IGMP groups list + */ +void +igmp_dump_group_list() +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state))); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + group = group->next; + } + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); +} +#else +#define igmp_dump_group_list() +#endif /* LWIP_DEBUG */ + +/** + * Start IGMP processing on interface + * + * @param netif network interface on which start IGMP processing + */ +err_t +igmp_start(struct netif *netif) +{ + struct igmp_group* group; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif)); + + group = igmp_lookup_group(netif, &allsystems); + + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, &allsystems); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER); + } + + return ERR_OK; + } + + return ERR_MEM; +} + +/** + * Stop IGMP processing on interface + * + * @param netif network interface on which stop IGMP processing + */ +err_t +igmp_stop(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + struct igmp_group *prev = NULL; + struct igmp_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == igmp_group_list) { + igmp_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report IGMP memberships for this interface + * + * @param netif network interface on which report IGMP memberships + */ +void +igmp_report_groups(struct netif *netif) +{ + struct igmp_group *group = igmp_group_list; + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif)); + + while (group != NULL) { + if (group->netif == netif) { + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + } + group = group->next; + } +} + +/** + * Search for a group in the global igmp_group_list + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search for + * @return a struct igmp_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct igmp_group * +igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + /* to be clearer, we return NULL here instead of + * 'group' (which is also NULL at this point). + */ + return NULL; +} + +/** + * Search for a specific igmp group and create a new one if not found- + * + * @param ifp the network interface for which to look + * @param addr the group ip address to search + * @return a struct igmp_group*, + * NULL on memory error. + */ +struct igmp_group * +igmp_lookup_group(struct netif *ifp, ip_addr_t *addr) +{ + struct igmp_group *group = igmp_group_list; + + /* Search if the group already exists */ + group = igmp_lookfor_group(ifp, addr); + if (group != NULL) { + /* Group already exists. */ + return group; + } + + /* Group doesn't exist yet, create a new one */ + group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->netif = ifp; + ip_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = igmp_group_list; + + igmp_group_list = group; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to "))); + ip_addr_debug_print(IGMP_DEBUG, addr); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp)); + + return group; +} + +/** + * Remove a group in the global igmp_group_list + * + * @param group the group to remove from the global igmp_group_list + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +igmp_remove_group(struct igmp_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (igmp_group_list == group) { + igmp_group_list = group->next; + } else { + /* look for group further down the list */ + struct igmp_group *tmpGroup; + for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not found in the global igmp_group_list */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_IGMP_GROUP, group); + + return err; +} + +/** + * Called from ip_input() if a new IGMP packet is received. + * + * @param p received igmp packet, p->payload pointing to the igmp header + * @param inp network interface on which the packet was received + * @param dest destination ip address of the igmp packet + */ +void +igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) +{ + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + IGMP_STATS_INC(igmp.recv); + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + if (p->len < IGMP_MINLEN) { + pbuf_free(p); + IGMP_STATS_INC(igmp.lenerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); + return; + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); + + /* Now calculate and check the checksum */ + igmp = (struct igmp_msg *)p->payload; + if (inet_chksum(igmp, p->len)) { + pbuf_free(p); + IGMP_STATS_INC(igmp.chkerr); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); + return; + } + + /* Packet is ok so find an existing group */ + group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + IGMP_STATS_INC(igmp.drop); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); + return; + } + + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: { + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + + if (igmp->igmp_maxresp == 0) { + IGMP_STATS_INC(igmp.rx_v1); + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); + igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; + } else { + IGMP_STATS_INC(igmp.rx_general); + } + + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { + igmp_delaying_member(groupref, igmp->igmp_maxresp); + } + groupref = groupref->next; + } + } else { + /* IGMP_MEMB_QUERY to a specific group ? */ + if (!ip_addr_isany(&igmp->igmp_group_address)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); + if (ip_addr_cmp(dest, &allsystems)) { + ip_addr_t groupaddr; + LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + /* we first need to re-look for the group since we used dest last time */ + ip_addr_copy(groupaddr, igmp->igmp_group_address); + group = igmp_lookfor_group(inp, &groupaddr); + } else { + LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); + } + + if (group != NULL) { + IGMP_STATS_INC(igmp.rx_group); + igmp_delaying_member(group, igmp->igmp_maxresp); + } else { + IGMP_STATS_INC(igmp.drop); + } + } else { + IGMP_STATS_INC(igmp.proterr); + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + IGMP_STATS_INC(igmp.rx_report); + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + /* This is on a specific group we have already looked up */ + group->timer = 0; /* stopped */ + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + break; + } + default: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", + igmp->igmp_msgtype, group->group_state, &group, group->netif)); + IGMP_STATS_INC(igmp.proterr); + break; + } + } + + pbuf_free(p); + return; +} + +/** + * Join a group on one network interface. + * + * @param ifaddr ip address of the network interface which should join a new group + * @param groupaddr the ip address of the group which to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group or create a new one if not found */ + group = igmp_lookup_group(netif, groupaddr); + + if (group != NULL) { + /* This should create a new group, check the state to make sure */ + if (group->group_state != IGMP_GROUP_NON_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n")); + } else { + /* OK - it was new group */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If first use of the group, allow the group at the MAC level */ + if ((group->use==0) && (netif->igmp_mac_filter != NULL)) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER); + } + + IGMP_STATS_INC(igmp.tx_join); + igmp_send(group, IGMP_V2_MEMB_REPORT); + + igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } + /* Increment group use */ + group->use++; + /* Join on this interface */ + err = ERR_OK; + } else { + /* Return an error even if some network interfaces are joined */ + /** @todo undo any other netif already joined */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n")); + return ERR_MEM; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on one network interface. + * + * @param ifaddr ip address of the network interface which should leave a group + * @param groupaddr the ip address of the group which to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct igmp_group *group; + struct netif *netif; + + /* make sure it is multicast address */ + LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;); + LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;); + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) { + /* find group */ + group = igmp_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Only send a leave if the flag is set according to the state diagram */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* If there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n")); + IGMP_STATS_INC(igmp.tx_leave); + igmp_send(group, IGMP_LEAVE_GROUP); + } + + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif)); + netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER); + } + + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: ")); + ip_addr_debug_print(IGMP_DEBUG, groupaddr); + LWIP_DEBUGF(IGMP_DEBUG, ("\n")); + + /* Free the group */ + igmp_remove_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } else { + /* It's not a fatal error on "leavegroup" */ + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n")); + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * The igmp timer function (both for NO_SYS=1 and =0) + * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default). + */ +void +igmp_tmr(void) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + igmp_timeout(group); + } + } + group = group->next; + } +} + +/** + * Called if a timeout for one group is reached. + * Sends a report for this group. + * + * @param group an igmp_group for which a timeout is reached + */ +static void +igmp_timeout(struct igmp_group *group) +{ + /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address ")); + ip_addr_debug_print(IGMP_DEBUG, &(group->group_address)); + LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif)); + + IGMP_STATS_INC(igmp.tx_report); + igmp_send(group, IGMP_V2_MEMB_REPORT); + } +} + +/** + * Start a timer for an igmp group + * + * @param group the igmp_group for which to start a timer + * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with + * every call to igmp_tmr()) + */ +static void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /* ensure the input value is > 0 */ + if (max_time == 0) { + max_time = 1; + } +#ifdef LWIP_RAND + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % (max_time - 1)) + 1; +#endif /* LWIP_RAND */ +} + +/** + * Delaying membership report for a group if necessary + * + * @param group the igmp_group for which "delaying" membership report + * @param maxresp query delay + */ +static void +igmp_delaying_member(struct igmp_group *group, u8_t maxresp) +{ + if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) || + ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + igmp_start_timer(group, maxresp); + group->group_state = IGMP_GROUP_DELAYING_MEMBER; + } +} + + +/** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, + * the IP address of the outgoing network interface is filled in as source address. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + */ +static err_t +igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = PP_HTONS(ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + IGMP_STATS_INC(igmp.xmit); + return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN); +} + +/** + * Send an igmp packet to a specific group. + * + * @param group the group to which to send the packet + * @param type the type of igmp packet to send + */ +static void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + ip_addr_t src = *IP_ADDR_ANY; + ip_addr_t* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = (struct igmp_msg *)p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_copy(src, group->netif->ip_addr); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + ip_addr_copy(igmp->igmp_group_address, group->group_address); + group->last_reporter_flag = 1; /* Remember we were the last to report */ + } else { + if (type == IGMP_LEAVE_GROUP) { + dest = &allrouters; + ip_addr_copy(igmp->igmp_group_address, group->group_address); + } + } + + if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) { + igmp->igmp_msgtype = type; + igmp->igmp_maxresp = 0; + igmp->igmp_checksum = 0; + igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN); + + igmp_ip_output_if(p, &src, dest, group->netif); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + IGMP_STATS_INC(igmp.memerr); + } +} + +#endif /* LWIP_IGMP */ diff --git a/external/badvpn_dns/lwip/src/core/ipv4/ip4.c b/external/badvpn_dns/lwip/src/core/ipv4/ip4.c new file mode 100644 index 00000000..1acc2558 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/ip4.c @@ -0,0 +1,924 @@ +/** + * @file + * This is the IPv4 layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip_frag.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/igmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** Set this to 0 in the rare case of wanting to call an extra function to + * generate the IP checksum (in contrast to calculating it on-the-fly). */ +#ifndef LWIP_INLINE_IP_CHKSUM +#define LWIP_INLINE_IP_CHKSUM 1 +#endif +#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP_INLINE 1 +#else +#define CHECKSUM_GEN_IP_INLINE 0 +#endif + +#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT) +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1 + +/** Some defines for DHCP to let link-layer-addressed packets through while the + * netif is down. + * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT + * to return 1 if the port is accepted and 0 if the port is not accepted. + */ +#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) +/* accept DHCP client port and custom port */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \ + || (LWIP_IP_ACCEPT_UDP_PORT(port))) +#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept custom port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port)) +#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ +/* accept DHCP client port only */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT)) +#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */ + +#else /* LWIP_DHCP */ +#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0 +#endif /* LWIP_DHCP */ + +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; + +/** The IP header ID of the next outgoing IP packet */ +static u16_t ip_id; + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + * + * @param dest the destination IP address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip_route(ip_addr_t *dest) +{ + struct netif *netif; + +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + /* iterate through netifs */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (netif_is_up(netif)) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + } + if ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + return netif_default; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr())); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IP header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + if (!ip_canforward(p)) { + goto return_noroute; + } + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(ip_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(ip_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + goto return_noroute; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + snmp_inc_ipinhdrerrors(); +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + return; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ip_current_dest_addr()); + return; +return_noroute: + snmp_inc_ipoutnoroutes(); +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IP packet (p->payload points to IP header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdr_hlen; + u16_t iphdr_len; +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = (struct ip_hdr *)p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* obtain IP header length in number of 32-bit words */ + iphdr_hlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdr_hlen *= 4; + /* obtain ip length in bytes */ + iphdr_len = ntohs(IPH_LEN(iphdr)); + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { + if (iphdr_hlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_hlen, p->len)); + } + if (iphdr_len > p->tot_len) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + iphdr_len, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdr_hlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinhdrerrors(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, iphdr_len); + + /* copy IP addresses to aligned ip_addr_t */ + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(ip_current_dest_addr())) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { + netif = inp; + } else { + netif = NULL; + } + } else +#endif /* LWIP_IGMP */ + { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(&netif->ip_addr), + ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask), + ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { + /* unicast to this interface address? */ + if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(ip_current_dest_addr(), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#if LWIP_AUTOIP + /* connections to link-local addresses must persist after changing + the netif's address (RFC3927 ch. 1.9) */ + if ((netif->autoip != NULL) && + ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } +#endif /* LWIP_AUTOIP */ + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + * + * If you want to accept private broadcast communication while a netif is down, + * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.: + * + * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345)) + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(udphdr->dest))); + if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if IP_ACCEPT_LINK_LAYER_ADDRESSING + /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */ + if (check_ip_src && !ip_addr_isany(ip_current_src_addr())) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || + (ip_addr_ismulticast(ip_current_src_addr()))) { + /* packet source is not valid */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n")); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.drop); + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + return ERR_OK; + } + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IPH_PROTO(iphdr) == IP_PROTO_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } else +#endif /* IP_FORWARD */ + { + snmp_inc_ipinaddrerrors(); + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = (struct ip_hdr *)p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ + +#if LWIP_IGMP + /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ + if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { +#else + if (iphdr_hlen > IP_HLEN) { +#endif /* LWIP_IGMP */ + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + /* unsupported protocol feature */ + snmp_inc_ipinunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS_ALLOWED == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + ip_data.current_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */ + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: +#if LWIP_UDPLITE + case IP_PROTO_UDPLITE: +#endif /* LWIP_UDPLITE */ + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ +#if LWIP_IGMP + case IP_PROTO_IGMP: + igmp_input(p, inp, ip_current_dest_addr()); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && + !ip_addr_ismulticast(ip_current_dest_addr())) { + pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipinunknownprotos(); + } + } + + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip_addr_set_any(ip_current_src_addr()); + ip_addr_set_any(ip_current_dest_addr()); + + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + * If the destination IP address is IP_HDRINCL, p is assumed to already + * include an IP header and p->payload points to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IP/LINK headers + * returns errors returned by netif->output + * + * @note ip_id: RFC791 "some host may be able to simply use + * unique identifiers independent of destination" + */ +err_t +ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ +#if IP_OPTIONS_SEND + return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if() but with the possibility to include IP options: + * + * @ param ip_options pointer to the IP options, copied into the IP header + * @ param optlen length of ip_options + */ +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen) +{ +#endif /* IP_OPTIONS_SEND */ + struct ip_hdr *iphdr; + ip_addr_t dest_addr; +#if CHECKSUM_GEN_IP_INLINE + u32_t chk_sum = 0; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + snmp_inc_ipoutrequests(); + + /* Should the IP header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + u16_t ip_hlen = IP_HLEN; +#if IP_OPTIONS_SEND + u16_t optlen_aligned = 0; + if (optlen != 0) { +#if CHECKSUM_GEN_IP_INLINE + int i; +#endif /* CHECKSUM_GEN_IP_INLINE */ + /* round up to a multiple of 4 */ + optlen_aligned = ((optlen + 3) & ~3); + ip_hlen += optlen_aligned; + /* First write in the IP options */ + if (pbuf_header(p, optlen_aligned)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n")); + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + MEMCPY(p->payload, ip_options, optlen); + if (optlen < optlen_aligned) { + /* zero the remaining bytes */ + memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen); + } +#if CHECKSUM_GEN_IP_INLINE + for (i = 0; i < optlen_aligned/2; i++) { + chk_sum += ((u16_t*)p->payload)[i]; + } +#endif /* CHECKSUM_GEN_IP_INLINE */ + } +#endif /* IP_OPTIONS_SEND */ + /* generate IP header */ + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = (struct ip_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", + (p->len >= sizeof(struct ip_hdr))); + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(proto, ttl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + + /* dest cannot be NULL here */ + ip_addr_copy(iphdr->dest, *dest); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16; +#endif /* CHECKSUM_GEN_IP_INLINE */ + + IPH_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_LEN_SET(iphdr, htons(p->tot_len)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_len; +#endif /* CHECKSUM_GEN_IP_INLINE */ + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += iphdr->_id; +#endif /* CHECKSUM_GEN_IP_INLINE */ + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_copy(iphdr->src, netif->ip_addr); + } else { + /* src cannot be NULL here */ + ip_addr_copy(iphdr->src, *src); + } + +#if CHECKSUM_GEN_IP_INLINE + chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF; + chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16; + chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF); + chk_sum = (chk_sum >> 16) + chk_sum; + chk_sum = ~chk_sum; + iphdr->_chksum = chk_sum; /* network order */ +#else /* CHECKSUM_GEN_IP_INLINE */ + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif +#endif /* CHECKSUM_GEN_IP_INLINE */ + } else { + /* IP header already included in p */ + iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy(dest_addr, iphdr->dest); + dest = &dest_addr; + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + +#if ENABLE_LOOPBACK + if (ip_addr_cmp(dest, &netif->ip_addr)) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()")); + return netif_loop_output(netif, p, dest); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p, dest); + } +#endif /* LWIP_IGMP */ +#endif /* ENABLE_LOOPBACK */ +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + return ip_frag(p, netif, dest); + } +#endif /* IP_FRAG */ + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an IP + header and p->payload points to that IP header) + * @param src the source IP address to send from (if src == IP_ADDR_ANY, the + * IP address of the netif used to send is used as source address) + * @param dest the destination IP address to send the packet to + * @param ttl the TTL value to be set in the IP header + * @param tos the TOS value to be set in the IP header + * @param proto the PROTOCOL to be set in the IP header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest))); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +/* Print an IP header by using LWIP_DEBUGF + * @param p an IP packet, p->payload pointing to the IP header + */ +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1_16(&iphdr->src), + ip4_addr2_16(&iphdr->src), + ip4_addr3_16(&iphdr->src), + ip4_addr4_16(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1_16(&iphdr->dest), + ip4_addr2_16(&iphdr->dest), + ip4_addr3_16(&iphdr->dest), + ip4_addr4_16(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/external/badvpn_dns/lwip/src/core/ipv4/ip4_addr.c b/external/badvpn_dns/lwip/src/core/ipv4/ip4_addr.c new file mode 100644 index 00000000..8f633ff2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/ip4_addr.c @@ -0,0 +1,312 @@ +/** + * @file + * This is the IPv4 address tools implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const ip_addr_t ip_addr_any = { IPADDR_ANY }; +const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST }; + +/** + * Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + */ +u8_t +ip4_addr_isbroadcast(u32_t addr, const struct netif *netif) +{ + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, addr); + + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr == IPADDR_ANY) || + (addr == IPADDR_ANY)) { + return 1; + /* no broadcast support on this network interface? */ + } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) { + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) { + return 0; + /* on the same (sub) network... */ + } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr & ~ip4_addr_get_u32(&netif->netmask)) == + (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) { + /* => network broadcast address */ + return 1; + } else { + return 0; + } +} + +/** Checks if a netmask is valid (starting with ones, then only zeros) + * + * @param netmask the IPv4 netmask to check (in network byte order!) + * @return 1 if the netmask is valid, 0 if it is not + */ +u8_t +ip4_addr_netmask_valid(u32_t netmask) +{ + u32_t mask; + u32_t nm_hostorder = lwip_htonl(netmask); + + /* first, check for the first zero */ + for (mask = 1UL << 31 ; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) == 0) { + break; + } + } + /* then check that there is no one */ + for (; mask != 0; mask >>= 1) { + if ((nm_hostorder & mask) != 0) { + /* there is a one after the first zero -> invalid */ + return 0; + } + } + /* no one after the first zero -> valid */ + return 1; +} + +/* Here for now until needed in other places in lwIP */ +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + +/** + * Ascii internet address interpretation routine. + * The value returned is in network order. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @return ip address in network order + */ +u32_t +ipaddr_addr(const char *cp) +{ + ip_addr_t val; + + if (ipaddr_aton(cp, &val)) { + return ip4_addr_get_u32(&val); + } + return (IPADDR_NONE); +} + +/** + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + * + * @param cp IP address in ascii represenation (e.g. "127.0.0.1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ipaddr_aton(const char *cp, ip_addr_t *addr) +{ + u32_t val; + u8_t base; + char c; + u32_t parts[4]; + u32_t *pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, 1-9=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; + base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') { + base = 16; + c = *++cp; + } else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (int)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) { + return (0); + } + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) { + return (0); + } + /* + * Concoct the address according to + * the number of parts specified. + */ + switch (pp - parts + 1) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffUL) { + return (0); + } + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) { + return (0); + } + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + default: + LWIP_ASSERT("unhandled", 0); + break; + } + if (addr) { + ip4_addr_set_u32(addr, htonl(val)); + } + return (1); +} + +/** + * Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ipaddr_ntoa(const ip_addr_t *addr) +{ + static char str[16]; + return ipaddr_ntoa_r(addr, str, 16); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen) +{ + u32_t s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + int len = 0; + + s_addr = ip4_addr_get_u32(addr); + + rp = buf; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) { + if (len++ >= buflen) { + return NULL; + } + *rp++ = inv[i]; + } + if (len++ >= buflen) { + return NULL; + } + *rp++ = '.'; + ap++; + } + *--rp = 0; + return buf; +} diff --git a/external/badvpn_dns/lwip/src/core/ipv4/ip_frag.c b/external/badvpn_dns/lwip/src/core/ipv4/ip_frag.c new file mode 100644 index 00000000..8d184345 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv4/ip_frag.c @@ -0,0 +1,863 @@ +/** + * @file + * This is the IPv4 packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * Simon Goldschmidt + * original reassembly code by Adam Dunkels + * + */ + +#include "lwip/opt.h" +#include "lwip/ip_frag.h" +#include "lwip/def.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "lwip/icmp.h" + +#include + +#if IP_REASSEMBLY +/** + * The IP reassembly code currently has the following limitations: + * - IP header options are not supported + * - fragments must not overlap (e.g. due to different routes), + * currently, overlapping or duplicate fragments are thrown away + * if IP_REASS_CHECK_OVERLAP=1 (the default)! + * + * @todo: work with IP header options + */ + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IP header, since it replaces + * the IP header in memory in incoming fragments (after copying it) to keep + * track of the various fragments. (-> If the IP header doesn't need packing, + * this struct doesn't need packing, too.) + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ + (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ + ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ + IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 + +/* global variables */ +static struct ip_reassdata *reassdatagrams; +static u16_t ip_reass_pbufcount; + +/* function prototypes */ +static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). + */ +void +ip_reass_tmr(void) +{ + struct ip_reassdata *r, *prev = NULL; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); + prev = r; + r = r->next; + } else { + /* reassembly timed out */ + struct ip_reassdata *tmp; + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip_reass_free_complete_datagram(tmp, prev); + } + } +} + +/** + * Free a datagram (struct ip_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip_reass_pbufcount), + * SNMP counters and sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + * @param prev the previous datagram in the linked list + * @return the number of pbufs freed + */ +static int +ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip_reass_helper *iprh; + + LWIP_ASSERT("prev != ipr", prev != ipr); + if (prev != NULL) { + LWIP_ASSERT("prev->next == ipr", prev->next == ipr); + } + + snmp_inc_ipreasmfails(); +#if LWIP_ICMP + iprh = (struct ip_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, copy the original header into it. */ + SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); + icmp_time_exceeded(p, ICMP_TE_FRAG); + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + /* Then, unchain the struct ip_reassdata from the list and free it. */ + ip_reass_dequeue_datagram(ipr, prev); + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); + ip_reass_pbufcount -= pbufs_freed; + + return pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram 'fraghdr' belongs to is not freed! + * + * @param fraghdr IP header of the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + * @return the number of pbufs freed + */ +static int +ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) +{ + /* @todo Can't we simply remove the last datagram in the + * linked list behind reassdatagrams? + */ + struct ip_reassdata *r, *oldest, *prev; + int pbufs_freed = 0, pbufs_freed_current; + int other_datagrams; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the datagram that 'fraghdr' belongs to! */ + do { + oldest = NULL; + prev = NULL; + other_datagrams = 0; + r = reassdatagrams; + while (r != NULL) { + if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { + /* Not the same datagram as fraghdr */ + other_datagrams++; + if (oldest == NULL) { + oldest = r; + } else if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + if (r->next != NULL) { + prev = r; + } + r = r->next; + } + if (oldest != NULL) { + pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); + pbufs_freed += pbufs_freed_current; + } + } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); + return pbufs_freed; +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Enqueues a new fragment into the fragment queue + * @param fraghdr points to the new fragments IP hdr + * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) + * @return A pointer to the queue location into which the fragment was enqueued + */ +static struct ip_reassdata* +ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) +{ + struct ip_reassdata* ipr; + /* No matching previous fragment found, allocate a new reassdata struct */ + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); + } + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IPFRAG_STATS_INC(ip_frag.memerr); + LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); + return NULL; + } + } + memset(ipr, 0, sizeof(struct ip_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + /* copy the ip header for later tests and input */ + /* @todo: no ip options supported? */ + SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); + return ipr; +} + +/** + * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. + * @param ipr points to the queue entry to dequeue + */ +static void +ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) +{ + + /* dequeue the reass struct */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", prev != NULL); + prev->next = ipr->next; + } + + /* now we can free the ip_reass struct */ + memp_free(MEMP_REASSDATA, ipr); +} + +/** + * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list + * will grow over time as new pbufs are rx. + * Also checks that the datagram passes basic continuity checks (if the last + * fragment was received at least once). + * @param root_p points to the 'root' pbuf for the current datagram being assembled. + * @param new_p points to the pbuf for the current fragment + * @return 0 if invalid, >0 otherwise + */ +static int +ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) +{ + struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct pbuf *q; + u16_t offset,len; + struct ip_hdr *fraghdr; + int valid = 1; + + /* Extract length and fragment offset from current fragment */ + fraghdr = (struct ip_hdr*)new_p->payload; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* overwrite the fragment's ip header from the pbuf with our helper struct, + * and setup the embedded helper structure. */ + /* make sure the struct ip_reass_helper fits into the IP header */ + LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", + sizeof(struct ip_reass_helper) <= IP_HLEN); + iprh = (struct ip_reass_helper*)new_p->payload; + iprh->next_pbuf = NULL; + iprh->start = offset; + iprh->end = offset + len; + + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ +#if IP_REASS_CHECK_OVERLAP + if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { + /* fragment overlaps with previous or following, throw away */ + goto freepbuf; + } +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + } else { + /* fragment with the lowest offset */ + ipr->p = new_p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + goto freepbuf; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + goto freepbuf; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no wholes. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = new_p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = new_p; + } + } + + /* At this point, the validation part begins: */ + /* If we already received the last fragment */ + if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { + /* and had no wholes so far */ + if (valid) { + /* then check if the rest of the fragments is here */ + /* Check if the queue starts with the first datagram */ + if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { + valid = 0; + } else { + /* and check that there are no wholes after this datagram */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while (q != NULL) { + iprh = (struct ip_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + /* if still valid, all fragments are received + * (because to the MF==0 already arrived */ + if (valid) { + LWIP_ASSERT("sanity check", ipr->p != NULL); + LWIP_ASSERT("sanity check", + ((struct ip_reass_helper*)ipr->p->payload) != iprh); + LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", + iprh->next_pbuf == NULL); + LWIP_ASSERT("validate_datagram:datagram end!=datagram len", + iprh->end == ipr->datagram_len); + } + } + } + /* If valid is 0 here, there are some fragments missing in the middle + * (since MF == 0 has already arrived). Such datagrams simply time out if + * no more fragments are received... */ + return valid; + } + /* If we come here, not all fragments were received, yet! */ + return 0; /* not yet valid! */ +#if IP_REASS_CHECK_OVERLAP +freepbuf: + ip_reass_pbufcount -= pbuf_clen(new_p); + pbuf_free(new_p); + return 0; +#endif /* IP_REASS_CHECK_OVERLAP */ +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *r; + struct ip_hdr *fraghdr; + struct ip_reassdata *ipr; + struct ip_reass_helper *iprh; + u16_t offset, len; + u8_t clen; + struct ip_reassdata *ipr_prev = NULL; + + IPFRAG_STATS_INC(ip_frag.recv); + snmp_inc_ipreasmreqds(); + + fraghdr = (struct ip_hdr*)p->payload; + + if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); + IPFRAG_STATS_INC(ip_frag.err); + goto nullreturn; + } + + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + + /* Check if we are allowed to enqueue more datagrams. */ + clen = pbuf_clen(p); + if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || + ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* No datagram could be freed and still too many pbufs enqueued */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", + ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); + IPFRAG_STATS_INC(ip_frag.memerr); + /* @todo: send ICMP time exceeded here? */ + /* drop this pbuf */ + goto nullreturn; + } + } + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); + /* Bail if unable to enqueue */ + if(ipr == NULL) { + goto nullreturn; + } + } else { + if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && + ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { + /* ipr->iphdr is not the header from the first fragment, but fraghdr is + * -> copy fraghdr into ipr->iphdr since we want to have the header + * of the first fragment (for ICMP time exceeded and later, for copying + * all options, if supported)*/ + SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); + } + } + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip_reass_pbufcount += clen; + + /* At this point, we have either created a new entry or pointing + * to an existing one */ + + /* check for 'no more fragments', and update queue entry*/ + if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { + ipr->flags |= IP_REASS_FLAG_LASTFRAG; + ipr->datagram_len = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ipr->datagram_len)); + } + /* find the right place to insert this pbuf */ + /* @todo: trim pbufs if fragments are overlapping */ + if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { + /* the totally last fragment (flag more fragments = 0) was received at least + * once AND all fragments are received */ + ipr->datagram_len += IP_HLEN; + + /* save the second pbuf before copying the header over the pointer */ + r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; + + /* copy the original ip header back to the first pbuf */ + fraghdr = (struct ip_hdr*)(ipr->p->payload); + SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); + IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); + IPH_OFFSET_SET(fraghdr, 0); + IPH_CHKSUM_SET(fraghdr, 0); + /* @todo: do we need to set calculate the correct checksum? */ + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); + + p = ipr->p; + + /* chain together the pbufs contained within the reass_data list. */ + while(r != NULL) { + iprh = (struct ip_reass_helper*)r->payload; + + /* hide the ip header for every succeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + /* release the sources allocate for the fragment queue entry */ + ip_reass_dequeue_datagram(ipr, ipr_prev); + + /* and adjust the number of pbufs currently queued for reassembly. */ + ip_reass_pbufcount -= pbuf_clen(p); + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); + return NULL; + +nullreturn: + LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if IP_FRAG_USES_STATIC_BUF +static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; +#else /* IP_FRAG_USES_STATIC_BUF */ + +#if !LWIP_NETIF_TX_SINGLE_PBUF +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ipfrag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip_frag_free_pbuf_custom_ref(pcr); +} +#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_REF) or + * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). + * + * @param p ip packet to send + * @param netif the netif on which to send + * @param dest destination ip address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else +#if !LWIP_NETIF_TX_SINGLE_PBUF + struct pbuf *newpbuf; +#endif + struct ip_hdr *original_iphdr; +#endif + struct ip_hdr *iphdr; + u16_t nfb; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF + u16_t newpbuflen = 0; + u16_t left_to_copy; +#endif + + /* Get a RAM based MTU sized pbuf */ +#if IP_FRAG_USES_STATIC_BUF + /* When using a static buffer, we use a PBUF_REF, which we will + * use to reference the packet (without link header). + * Layer and length is irrelevant. + */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = LWIP_MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = (struct ip_hdr *)rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = (struct ip_hdr *)p->payload; + iphdr = original_iphdr; +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + nfb = (mtu - IP_HLEN) / 8; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) { + tmp = tmp | IP_MF; + } + + /* Fill this fragment */ + cop = last ? left : nfb * 8; + +#if IP_FRAG_USES_STATIC_BUF + poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); +#else /* IP_FRAG_USES_STATIC_BUF */ +#if LWIP_NETIF_TX_SINGLE_PBUF + rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); + poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); + /* make room for the IP header */ + if(pbuf_header(rambuf, IP_HLEN)) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* fill in the IP header */ + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = rambuf->payload; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link and IP header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); + if (rambuf == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP_HLEN))); + SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); + iphdr = (struct ip_hdr *)rambuf->payload; + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ +#endif /* IP_FRAG_USES_STATIC_BUF */ + + /* Correct header */ + IPH_OFFSET_SET(iphdr, htons(tmp)); + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + +#if IP_FRAG_USES_STATIC_BUF + if (last) { + pbuf_realloc(rambuf, left + IP_HLEN); + } + + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + snmp_inc_ipfragcreates(); + pbuf_free(header); + } else { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); + pbuf_free(rambuf); + return ERR_MEM; + } +#else /* IP_FRAG_USES_STATIC_BUF */ + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + netif->output(netif, rambuf, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + left -= cop; + ofo += nfb; + } +#if IP_FRAG_USES_STATIC_BUF + pbuf_free(rambuf); +#endif /* IP_FRAG_USES_STATIC_BUF */ + snmp_inc_ipfragoks(); + return ERR_OK; +} +#endif /* IP_FRAG */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/README b/external/badvpn_dns/lwip/src/core/ipv6/README new file mode 100644 index 00000000..36200048 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/external/badvpn_dns/lwip/src/core/ipv6/dhcp6.c b/external/badvpn_dns/lwip/src/core/ipv6/dhcp6.c new file mode 100644 index 00000000..9656c3b2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/dhcp6.c @@ -0,0 +1,50 @@ +/** + * @file + * + * DHCPv6. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + + +#endif /* LWIP_IPV6_DHCP6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/ethip6.c b/external/badvpn_dns/lwip/src/core/ipv6/ethip6.c new file mode 100644 index 00000000..ab9783a0 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/ethip6.c @@ -0,0 +1,193 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" + +#include + +#define ETHTYPE_IPV6 0x86DD + +/** The ethernet address */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Ethernet header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +/** + * Send an IPv6 packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!", + (netif->hwaddr_len == 6)); + SMEMCPY(ðhdr->dest, dst, 6); + SMEMCPY(ðhdr->src, src, 6); + ethhdr->type = PP_HTONS(ETHTYPE_IPV6); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ... + * + * @TODO anycast addresses + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ip6addr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + s8_t i; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + return ERR_BUF; + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We have a unicast destination IP address */ + /* TODO anycast? */ + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return ERR_MEM; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */ + if ((neighbor_cache[i].state == ND6_REACHABLE) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Send out. */ + SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We should queue packet on this interface. */ + pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); + return nd6_queue_packet(i, q); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/icmp6.c b/external/badvpn_dns/lwip/src/core/ipv6/icmp6.c new file mode 100644 index 00000000..ea82682e --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/icmp6.c @@ -0,0 +1,337 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if LWIP_ICMP6_CHECKSUM_CHECK + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_ICMP6_CHECKSUM_CHECK */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; + break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* TODO implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; + break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t *reply_src, *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(netif, reply_dest); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/inet6.c b/external/badvpn_dns/lwip/src/core/ipv6/inet6.c new file mode 100644 index 00000000..bdf4ff4f --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/inet6.c @@ -0,0 +1,51 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet6.h" + +/** @see ip6_addr.c for implementation of functions. */ + +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/ip6.c b/external/badvpn_dns/lwip/src/core/ipv6/ip6.c new file mode 100644 index 00000000..1eb91f96 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/ip6.c @@ -0,0 +1,1034 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(struct ip6_addr *src, struct ip6_addr *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif. */ + return netif_default; + } + + /* Try to find the netif for the source address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif */ + return netif_default; + } + + /* See if the destination subnet matches a configured address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* Get the netif for a suitable router. */ + i = nd6_select_router(dest, NULL); + if (i >= 0) { + if (default_router_list[i].neighbor_entry != NULL) { + if (default_router_list[i].neighbor_entry->netif != NULL) { + return default_router_list[i].neighbor_entry->netif; + } + } + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +ip6_addr_t * +ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) +{ + ip6_addr_t * src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip6_addr(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* TODO find longest matching prefix. */ + if ((!(ip6_addr_netcmp(src, dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip6_addr(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + /* do not forward link-local addresses */ + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + + +/** + * This function is called by the network interface device driver when + * an IPv6 packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 header) + * @param inp the netif on which this packet was received + * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't + * processed, but currently always returns ERR_OK) + */ +err_t +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); + ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } + else { + /* start trying with inp. if that's not acceptable, start walking the + list of configured netifs. + 'first' is used as a boolean to mark whether we started walking the list */ + int first = 1; + netif = inp; + do { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + /* Do not match link-local addresses to other netifs. */ + netif = NULL; + break; + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it + isn't for a local address */ + if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IP6H_NEXTH(ip6hdr) == IP6_NEXTH_TCP) { + netif = inp; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr * frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && + ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { + + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -hlen); + } + else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + pbuf_header(p, ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); + ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points + * to it instead of the data. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param netif the netif on which to send this packet + * @return ERR_OK if the packet was sent OK + * ERR_BUF if p doesn't have enough space for IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY; + } + else if (ip6_addr_isany(src)) { + src = ip6_select_source_address(netif, dest); + if ((src == NULL) || ip6_addr_isany(src)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + /* TODO implement loopback for v6 + if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { + return netif_loop_output(netif, p, dest); + }*/ +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_output_if to do the actual work. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_output_if. + * + * @param p the packet to send (p->payload points to the data, e.g. next + protocol header; if dest == IP_HDRINCL, p already includes an + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * @param addr_hint address hint pointer set to netif->addr_hint before + * calling ip_output_if() + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + /* pbufs passed to IP must have a ref-count of 1 as their payload pointer + gets altered as the packet is passed down the stack */ + LWIP_ASSERT("p->ref == 1", p->ref == 1); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr * hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + IP6H_PLEN(ip6hdr), + IP6H_NEXTH(ip6hdr), + IP6H_HOPLIM(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/ip6_addr.c b/external/badvpn_dns/lwip/src/core/ipv6/ip6_addr.c new file mode 100644 index 00000000..65d27980 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/ip6_addr.c @@ -0,0 +1,251 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY in ip6_addr.h */ +const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; + +#ifndef isprint +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isprint(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii represenation (e.g. "FF01::1") + * @param addr pointer to which to save the ip address in network order + * @return 1 if cp could be converted to addr, 0 on failure + */ +int +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char * s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') + zero_blocks--; + else if (!isxdigit(*s)) + break; + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } if (s[1] == ':') { + s++; + /* "::" found, set zeros */ + while (zero_blocks-- > 0) { + if (current_block_index & 0x1) { + addr_index++; + } + else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + } + } + } else if (isxdigit(*s)) { + /* add current digit */ + current_block_value = (current_block_value << 4) + + (isdigit(*s) ? *s - '0' : + 10 + (islower(*s) ? *s - 'a' : *s - 'A')); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * represenation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 address in network order to convert + * @param buf target buffer where the string is stored + * @param buflen length of buf + * @return either pointer to buf which now holds the ASCII + * representation of addr or NULL if buf was too small + */ +char * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; + + i = 0; + zero_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + if (current_block_value == 0) { + /* generate empty block "::" */ + if (!zero_flag) { + if (current_block_index > 0) { + zero_flag = 1; + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + } + } + else { + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } + else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) return NULL; + + zero_flag = 0; + } + } + + buf[i] = 0; + + return buf; +} +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/ip6_frag.c b/external/badvpn_dns/lwip/src/core/ipv6/ip6_frag.c new file mode 100644 index 00000000..a43fd614 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/ip6_frag.c @@ -0,0 +1,697 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** Setting this to 0, you can turn off checking the fragments for overlapping + * regions. The code gets a little smaller. Only use this if you know that + * overlapping won't occur on your network! */ +#ifndef IP_REASS_CHECK_OVERLAP +#define IP_REASS_CHECK_OVERLAP 1 +#endif /* IP_REASS_CHECK_OVERLAP */ + +/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is + * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. + * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA + * is set to 1, so one datagram can be reassembled at a time, only. */ +#ifndef IP_REASS_FREE_OLDEST +#define IP_REASS_FREE_OLDEST 1 +#endif /* IP_REASS_FREE_OLDEST */ + +#define IP_REASS_FLAG_LASTFRAG 0x01 + +/** This is a helper struct which holds the starting + * offset and the ending offset of this fragment to + * easily chain the fragments. + * It has the same packing requirements as the IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_reass_helper { + PACK_STRUCT_FIELD(struct pbuf *next_pbuf); + PACK_STRUCT_FIELD(u16_t start); + PACK_STRUCT_FIELD(u16_t end); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* static variables */ +static struct ip6_reassdata *reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_reass_helper *)ipr->p->payload; + if (iprh->start == 0) { + /* The first fragment was received, send ICMP time exceeded. */ + /* First, de-queue the first pbuf from r->p. */ + p = ipr->p; + ipr->p = iprh->next_pbuf; + /* Then, move back to the original header (we are now pointing to Fragment header). */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* First, free all received pbufs. The individual pbufs need to be released + separately as they have not yet been chained */ + p = ipr->p; + while (p != NULL) { + struct pbuf *pcur; + iprh = (struct ip6_reass_helper *)p->payload; + pcur = p; + /* get the next pointer before freeing */ + p = iprh->next_pbuf; + clen = pbuf_clen(pcur); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(pcur); + } + + /* Then, unchain the struct ip6_reassdata from the list and free it. */ + if (ipr == reassdatagrams) { + reassdatagrams = ipr->next; + } else { + prev = reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @param len the length of the payload (after Fragment Header) + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr * frag_hdr; + u16_t offset, len; + u8_t clen, valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = ntohs(ip6_current_header()->_plen); + len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; + len -= IP6_FRAG_HLEN; + + /* Look for the datagram the fragment belongs to in the current datagram queue, + * remembering the previous in the queue for later dequeueing. */ + for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { + /* Check if the incoming fragment matches the one currently present + in the reassembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ + iprh = (struct ip6_reass_helper *)p->payload; + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* Iterate through until we either get to the end of the list (append), + * or we find on with a larger offset (insert). */ + for (q = ipr->p; q != NULL;) { + iprh_tmp = (struct ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + if (iprh_prev != NULL) { + if (iprh_prev->end != iprh_tmp->start) { + /* There is a fragment missing between the current + * and the previous fragment */ + valid = 0; + } + } + } + q = iprh_tmp->next_pbuf; + iprh_prev = iprh_tmp; + } + + /* If q is NULL, then we made it to the end of the list. Determine what to do now */ + if (q == NULL) { + if (iprh_prev != NULL) { + /* this is (for now), the fragment with the highest offset: + * chain it to the last fragment */ +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); +#endif /* IP_REASS_CHECK_OVERLAP */ + iprh_prev->next_pbuf = p; + if (iprh_prev->end != iprh->start) { + valid = 0; + } + } else { +#if IP_REASS_CHECK_OVERLAP + LWIP_ASSERT("no previous fragment, this must be the first fragment!", + ipr->p == NULL); +#endif /* IP_REASS_CHECK_OVERLAP */ + /* this is the first fragment we ever received for this ip datagram */ + ipr->p = p; + } + } + + /* Track the current number of pbufs current 'in-flight', in order to limit + the number of fragments that may be enqueued at any one time */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while(iprh != NULL) { + + if (iprh->next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; + + /* hide the fragment header for every succeding fragment */ + pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); + pbuf_cat(ipr->p, iprh->next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + + IP6_FRAG_HLEN + - IP6_HLEN ; + + /* Set payload length in ip header. */ + ipr->iphdr->_plen = htons(ipr->datagram_len); + + /* Get the furst pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + if (reassdatagrams == ipr) { + /* it was the first in the list */ + reassdatagrams = ipr->next; + } else { + /* it wasn't the first, so it must have a valid 'prev' */ + LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) +{ + LWIP_ASSERT("p != NULL", p != NULL); + memp_free(MEMP_FRAG_PBUF, p); +} + +/** Free-callback function to free a 'struct pbuf_custom_ref', called by + * pbuf_free. */ +static void +ip6_frag_free_pbuf_custom(struct pbuf *p) +{ + struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; + LWIP_ASSERT("pcr != NULL", pcr != NULL); + LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); + if (pcr->original != NULL) { + pbuf_free(pcr->original); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr * frag_hdr; + struct pbuf *rambuf; + struct pbuf *newpbuf; + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + u16_t newpbuflen = 0; + u16_t left_to_copy; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header. + * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, + * but limited to the size of an mtu. + */ + rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_len -= poff; + + left_to_copy = cop; + while (left_to_copy) { + struct pbuf_custom_ref *pcr; + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + pcr = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); + if (newpbuf == NULL) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom; + + /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain + * so that it is removed when pbuf_dechain is later called on rambuf. + */ + pbuf_cat(rambuf, newpbuf); + left_to_copy -= newpbuflen; + if (left_to_copy) { + p = p->next; + } + } + poff = newpbuflen; + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* Unfortunately we can't reuse rambuf - the hardware may still be + * using the buffer. Instead we free it (and the ensuing chain) and + * recreate it next time round the loop. If we're lucky the hardware + * will have already sent the packet, the free will really free, and + * there will be zero memory penalty. + */ + + pbuf_free(rambuf); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/mld6.c b/external/badvpn_dns/lwip/src/core/ipv6/mld6.c new file mode 100644 index 00000000..1cb2dd9c --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/mld6.c @@ -0,0 +1,580 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + + +/* The list of joined groups. */ +static struct mld_group* mld_group_list; + + +/* Forward declarations. */ +static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); +static err_t mld6_free_group(struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + struct mld_group *prev = NULL; + struct mld_group *next; + + /* look for groups joined on this interface further down the list */ + while (group != NULL) { + next = group->next; + /* is it a group joined on this interface? */ + if (group->netif == netif) { + /* is it the first group of the list? */ + if (group == mld_group_list) { + mld_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->netif == netif) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + group->netif = ifp; + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = mld_group_list; + + mld_group_list = group; + } + + return group; +} + +/** + * Remove a group in the mld_group_list and free + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_free_group(struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (mld_group_list == group) { + mld_group_list = group->next; + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header * mld_hdr; + struct mld_group* group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* TODO debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + { + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = mld_group_list; + while (group != NULL) { + if ((group->netif == inp) && + (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } + else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + } + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + } + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + { + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + } + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + err = ERR_OK; + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave if there is no other use of the group */ + if (group->use <= 1) { + /* If we are the last reporter for this group */ + if (group->last_reporter_flag) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); + } + + /* Free the group */ + mld6_free_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = (LWIP_RAND() % (maxresp - 1)) + 1; +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct mld_group *group, u8_t type) +{ + struct mld_header * mld_hdr; + struct pbuf * p; + ip6_addr_t * src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { + /* We couldn't allocate a suitable pbuf. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(group->netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); + pbuf_free(p); +} + + + +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/ipv6/nd6.c b/external/badvpn_dns/lwip/src/core/ipv6/nd6.c new file mode 100644 index 00000000..480638e9 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/ipv6/nd6.c @@ -0,0 +1,1787 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/stats.h" + +#include + + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); +static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); +static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static void nd6_send_rs(struct netif * netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + /* We are using a duplicate address. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + +#if LWIP_IPV6_MLD + /* Leave solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]); + mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address); +#endif /* LWIP_IPV6_MLD */ + + + + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } + else { + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + neighbor_cache[i].netif = inp; + neighbor_cache[i].counter.reachable_time = reachable_time; + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + neighbor_cache[i].state = ND6_REACHABLE; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + lladdr_opt = NULL; + if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } + else { + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if ( i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + else + { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header * ra_hdr; + u8_t * buffer; /* Used to copy options. */ + u16_t offset; + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + inp->rs_count = 0; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = ra_hdr->retrans_timer; + } + if (ra_hdr->reachable_time > 0) { + reachable_time = ra_hdr->reachable_time; + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* TODO set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option * lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option * mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (mtu_opt->mtu >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = mtu_opt->mtu; +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option * prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { + /* Add to on-link prefix list. */ + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i < 0) { + /* Create a new cache entry. */ + i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); + } + if (i >= 0) { + prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime; + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + { + /* TODO implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; + } + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header * redir_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + lladdr_opt = NULL; + if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + destination_cache[i].pmtu = icmp6hdr->data; + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i, j; + struct netif * netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } + else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } + else { + neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_PROBE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remainig. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + prefix_list[i].invalidation_timer = 0; + } + if ((prefix_list[i].invalidation_timer > 0) && + (prefix_list[i].netif != NULL)) { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; + prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; + prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; + prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_istentative(netif->ip6_addr_state[i])) { + if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; + /* TODO implement preferred and valid lifetimes. */ + } + else if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_IPV6_MLD + if ((netif->ip6_addr_state[i] & 0x07) == 0) { + /* Join solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); + mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); + } +#endif /* LWIP_IPV6_MLD */ + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + (netif->ip6_addr_state[i])++; + /* TODO send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { + nd6_send_rs(netif); + netif->rs_count--; + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + ip6_addr_t * dest_addr; + + /* Use link-local address as source address. */ + /* src_addr = &(netif->ip6_addr[0]); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } + else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } + else { + dest_addr = ip6_current_src_addr(); + } + + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static void +nd6_send_rs(struct netif * netif) +{ + struct rs_header * rs_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + u16_t packet_len; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } + else { + src_addr = IP6_ADDR_ANY; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + packet_len = sizeof(struct rs_header); + if (src_addr != IP6_ADDR_ANY) { + packet_len += sizeof(struct lladdr_option); + } + p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); + if ((p == NULL) || (p->len < packet_len)) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +s8_t +nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* TODO: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 0; + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { + if (default_router_list[router_index].neighbor_entry == NULL) { + default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); + return router_index; + } + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); +#if LWIP_IPV6_AUTOCONFIG + prefix_list[i].flags = 0; +#endif + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +s8_t +nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } + else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, netif)) { + /* Destination in local link. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); + } + else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } + else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 0; + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM); + } + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if(neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(ip6_addr_t * ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/mem.c b/external/badvpn_dns/lwip/src/core/mem.c new file mode 100644 index 00000000..1659a2c7 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/mem.c @@ -0,0 +1,659 @@ +/** + * @file + * Dynamic memory manager + * + * This is a lightweight replacement for the standard C library malloc(). + * + * If you want to use the standard C library malloc() instead, define + * MEM_LIBC_MALLOC to 1 in your lwipopts.h + * + * To let mem_malloc() use pools (prevents fragmentation and is much faster than + * a heap but might waste some memory), define MEM_USE_POOLS to 1, define + * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list + * of pools like this (more pools can be added between _START and _END): + * + * Define three pools with sizes 256, 512, and 1512 bytes + * LWIP_MALLOC_MEMPOOL_START + * LWIP_MALLOC_MEMPOOL(20, 256) + * LWIP_MALLOC_MEMPOOL(10, 512) + * LWIP_MALLOC_MEMPOOL(5, 1512) + * LWIP_MALLOC_MEMPOOL_END + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/err.h" + +#include + +#if MEM_USE_POOLS +/* lwIP head implemented with different sized pools */ + +/** + * Allocate memory: determine the smallest pool that is big enough + * to contain an element of 'size' and get an element from that pool. + * + * @param size the size in bytes of the memory needed + * @return a pointer to the allocated memory or NULL if the pool is empty + */ +void * +mem_malloc(mem_size_t size) +{ + void *ret; + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { +#if MEM_USE_POOLS_TRY_BIGGER_POOL +again: +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + /* is this pool big enough to hold an element of the required size + plus a struct memp_malloc_helper that saves the pool this element came from? */ + if (required_size <= memp_sizes[poolnr]) { + break; + } + } + if (poolnr > MEMP_POOL_LAST) { + LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); + return NULL; + } + element = (struct memp_malloc_helper*)memp_malloc(poolnr); + if (element == NULL) { + /* No need to DEBUGF or ASSERT: This error is already + taken care of in memp.c */ +#if MEM_USE_POOLS_TRY_BIGGER_POOL + /** Try a bigger pool if this one is empty! */ + if (poolnr < MEMP_POOL_LAST) { + poolnr++; + goto again; + } +#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ + return NULL; + } + + /* save the pool number this element came from */ + element->poolnr = poolnr; + /* and return a pointer to the memory directly after the struct memp_malloc_helper */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + return ret; +} + +/** + * Free memory previously allocated by mem_malloc. Loads the pool number + * and calls memp_free with that pool number to put the element back into + * its pool + * + * @param rmem the memory element to free + */ +void +mem_free(void *rmem) +{ + struct memp_malloc_helper *hmem; + + LWIP_ASSERT("rmem != NULL", (rmem != NULL)); + LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); + + /* get the original struct memp_malloc_helper */ + hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + LWIP_ASSERT("hmem != NULL", (hmem != NULL)); + LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); + LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); + + /* and put it in the pool we saved earlier */ + memp_free(hmem->poolnr, hmem); +} + +#else /* MEM_USE_POOLS */ +/* lwIP replacement for your libc malloc() */ + +/** + * The heap is made up as a list of structs of this type. + * This does not have to be aligned since for getting its size, + * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes. + */ +struct mem { + /** index (-> ram[next]) of the next struct */ + mem_size_t next; + /** index (-> ram[prev]) of the previous struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** All allocated blocks will be MIN_SIZE bytes big, at least! + * MIN_SIZE can be overridden to suit your needs. Smaller values save space, + * larger values could prevent too small blocks to fragment the RAM too much. */ +#ifndef MIN_SIZE +#define MIN_SIZE 12 +#endif /* MIN_SIZE */ +/* some alignment macros: we define them here for better source code layout */ +#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE) +#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem)) +#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + +/** If you want to relocate the heap to external memory, simply define + * LWIP_RAM_HEAP_POINTER as a void-pointer to that location. + * If so, make sure the memory at that location is big enough (see below on + * how that space is calculated). */ +#ifndef LWIP_RAM_HEAP_POINTER +/** the heap. we need one struct mem at the end and some room for alignment */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +#define LWIP_RAM_HEAP_POINTER ram_heap +#endif /* LWIP_RAM_HEAP_POINTER */ + +/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */ +static u8_t *ram; +/** the last entry, always unused! */ +static struct mem *ram_end; +/** pointer to the lowest free block, this is used for faster search */ +static struct mem *lfree; + +/** concurrent access protection */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + +static volatile u8_t mem_free_count; + +/* Allow mem_free from other (e.g. interrupt) context */ +#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free) +#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free) +#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free) +#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc) +#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc) + +#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + +/* Protect the heap only by using a semaphore */ +#define LWIP_MEM_FREE_DECL_PROTECT() +#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex) +#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex) +/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */ +#define LWIP_MEM_ALLOC_DECL_PROTECT() +#define LWIP_MEM_ALLOC_PROTECT() +#define LWIP_MEM_ALLOC_UNPROTECT() + +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + +/** + * "Plug holes" by combining adjacent empty struct mems. + * After this function is through, there should not exist + * one empty struct mem pointing to another empty struct mem. + * + * @param mem this points to a struct mem which just has been freed + * @internal this function is only called by mem_free() and mem_trim() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED); + + nmem = (struct mem *)(void *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + /* if mem->next is unused and not end of ram, combine mem and mem->next */ + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); + } + + /* plug hole backward */ + pmem = (struct mem *)(void *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); + } +} + +/** + * Zero the heap and initialize start, end and lowest-free + */ +void +mem_init(void) +{ + struct mem *mem; + + LWIP_ASSERT("Sanity check alignment", + (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); + + /* align the heap */ + ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER); + /* initialize the start of the heap */ + mem = (struct mem *)(void *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)(void *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); + + if(sys_mutex_new(&mem_mutex) != ERR_OK) { + LWIP_ASSERT("failed to create mem_mutex", 0); + } +} + +/** + * Put a struct mem back on the heap + * + * @param rmem is the data portion of a struct mem as returned by a previous + * call to mem_malloc() + */ +void +mem_free(void *rmem) +{ + struct mem *mem; + LWIP_MEM_FREE_DECL_PROTECT(); + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n")); + return; + } + LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return; + } + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + LWIP_ASSERT("mem_free: mem->used", mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) { + /* the newly freed struct is now the lowest */ + lfree = mem; + } + + MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram))); + + /* finally, see if prev or next are free also */ + plug_holes(mem); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); +} + +/** + * Shrink memory returned by mem_malloc(). + * + * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked + * @param newsize required size after shrinking (needs to be smaller than or + * equal to the previous size) + * @return for compatibility reasons: is always == rmem, at the moment + * or NULL if newsize is > old size, in which case rmem is NOT touched + * or freed! + */ +void * +mem_trim(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ + LWIP_MEM_FREE_DECL_PROTECT(); + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + newsize = LWIP_MEM_ALIGN_SIZE(newsize); + + if(newsize < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + newsize = MIN_SIZE_ALIGNED; + } + + if (newsize > MEM_SIZE_ALIGNED) { + return NULL; + } + + LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + SYS_ARCH_DECL_PROTECT(lev); + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); + /* protect mem stats from concurrent access */ + SYS_ARCH_PROTECT(lev); + MEM_STATS_INC(illegal); + SYS_ARCH_UNPROTECT(lev); + return rmem; + } + /* Get the corresponding struct mem ... */ + mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (mem_size_t)((u8_t *)mem - ram); + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); + if (newsize > size) { + /* not supported */ + return NULL; + } + if (newsize == size) { + /* No change in size, simply return */ + return rmem; + } + + /* protect the heap from concurrent access */ + LWIP_MEM_FREE_PROTECT(); + + mem2 = (struct mem *)(void *)&ram[mem->next]; + if(mem2->used == 0) { + /* The next struct is unused, we can simply move it at little */ + mem_size_t next; + /* remember the old next pointer */ + next = mem2->next; + /* create new struct mem which is moved directly after the shrinked mem */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + if (lfree == mem2) { + lfree = (struct mem *)(void *)&ram[ptr2]; + } + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + /* restore the next pointer */ + mem2->next = next; + /* link it back to mem */ + mem2->prev = ptr; + /* link mem to it */ + mem->next = ptr2; + /* last thing to restore linked list: as we have moved mem2, + * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not + * the end of the heap */ + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* no need to plug holes, we've already done that */ + } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { + /* Next struct is used but there's room for another struct mem with + * at least MIN_SIZE_ALIGNED of data. + * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem + * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)(void *)&ram[ptr2]; + if (mem2 < lfree) { + lfree = mem2; + } + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_DEC_USED(used, (size - newsize)); + /* the original mem->next is used, so no need to plug holes! */ + } + /* else { + next struct mem is used but size between mem and mem2 is not big enough + to create another struct mem + -> don't do anyhting. + -> the remaining space stays unused since it is too small + } */ +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 1; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_FREE_UNPROTECT(); + return rmem; +} + +/** + * Adam's mem_malloc() plus solution for bug #17922 + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * @return pointer to allocated memory or NULL if no free memory was found. + * + * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). + */ +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + u8_t local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_MEM_ALLOC_DECL_PROTECT(); + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + size = LWIP_MEM_ALIGN_SIZE(size); + + if(size < MIN_SIZE_ALIGNED) { + /* every data block must be at least MIN_SIZE_ALIGNED long */ + size = MIN_SIZE_ALIGNED; + } + + if (size > MEM_SIZE_ALIGNED) { + return NULL; + } + + /* protect the heap from concurrent access */ + sys_mutex_lock(&mem_mutex); + LWIP_MEM_ALLOC_PROTECT(); +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* run as long as a mem_free disturbed mem_malloc or mem_trim */ + do { + local_mem_free_count = 0; +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + /* Scan through the heap searching for a free block that is big enough, + * beginning with the lowest free block. + */ + for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)(void *)&ram[ptr])->next) { + mem = (struct mem *)(void *)&ram[ptr]; +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* allow mem_free or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { + /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + /* create mem2 struct */ + mem2 = (struct mem *)(void *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != MEM_SIZE_ALIGNED) { + ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; + } + MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); + } else { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1; + MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + LWIP_ASSERT("mem_malloc: sanity check alignment", + (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); + + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + /* if we got interrupted by a mem_free, try again */ + } while(local_mem_free_count != 0); +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); + MEM_STATS_INC(err); + LWIP_MEM_ALLOC_UNPROTECT(); + sys_mutex_unlock(&mem_mutex); + return NULL; +} + +#endif /* MEM_USE_POOLS */ +/** + * Contiguously allocates enough space for count objects that are size bytes + * of memory each and returns a pointer to the allocated memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *mem_calloc(mem_size_t count, mem_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = mem_malloc(count * size); + if (p) { + /* zero the memory */ + memset(p, 0, count * size); + } + return p; +} + +#endif /* !MEM_LIBC_MALLOC */ diff --git a/external/badvpn_dns/lwip/src/core/memp.c b/external/badvpn_dns/lwip/src/core/memp.c new file mode 100644 index 00000000..1323463e --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/memp.c @@ -0,0 +1,485 @@ +/** + * @file + * Dynamic pool memory manager + * + * lwIP has dedicated pools for many structures (netconn, protocol control blocks, + * packet buffers, ...). All these pools are managed here. + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp_impl.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.h" +#include "lwip/snmp_structs.h" +#include "lwip/snmp_msg.h" +#include "lwip/dns.h" +#include "netif/ppp_oe.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" + +#include + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +struct memp { + struct memp *next; +#if MEMP_OVERFLOW_CHECK + const char *file; + int line; +#endif /* MEMP_OVERFLOW_CHECK */ +}; + +#if MEMP_OVERFLOW_CHECK +/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning + * and at the end of each element, initialize them as 0xcd and check + * them later. */ +/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free, + * every single element in each pool is checked! + * This is VERY SLOW but also very helpful. */ +/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in + * lwipopts.h to change the amount reserved for checking. */ +#ifndef MEMP_SANITY_REGION_BEFORE +#define MEMP_SANITY_REGION_BEFORE 16 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#if MEMP_SANITY_REGION_BEFORE > 0 +#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE) +#else +#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_BEFORE*/ +#ifndef MEMP_SANITY_REGION_AFTER +#define MEMP_SANITY_REGION_AFTER 16 +#endif /* MEMP_SANITY_REGION_AFTER*/ +#if MEMP_SANITY_REGION_AFTER > 0 +#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER) +#else +#define MEMP_SANITY_REGION_AFTER_ALIGNED 0 +#endif /* MEMP_SANITY_REGION_AFTER*/ + +/* MEMP_SIZE: save space for struct memp and for sanity check */ +#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED) +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED) + +#else /* MEMP_OVERFLOW_CHECK */ + +/* No sanity checks + * We don't need to preserve the struct memp while not allocated, so we + * can save a little space and set MEMP_SIZE to 0. + */ +#define MEMP_SIZE 0 +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_OVERFLOW_CHECK */ + +/** This array holds the first free element of each pool. + * Elements form a linked list. */ +static struct memp *memp_tab[MEMP_MAX]; + +#else /* MEMP_MEM_MALLOC */ + +#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x)) + +#endif /* MEMP_MEM_MALLOC */ + +/** This array holds the element sizes of each pool. */ +#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC +static +#endif +const u16_t memp_sizes[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */ + +/** This array holds the number of elements in each pool. */ +static const u16_t memp_num[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (num), +#include "lwip/memp_std.h" +}; + +/** This array holds a textual description of each pool. */ +#ifdef LWIP_DEBUG +static const char *memp_desc[MEMP_MAX] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +#if MEMP_SEPARATE_POOLS + +/** This creates each memory pool. These are named memp_memory_XXX_base (where + * XXX is the name of the pool defined in memp_std.h). + * To relocate a pool, declare it as extern in cc.h. Example for GCC: + * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[]; + */ +#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; +#include "lwip/memp_std.h" + +/** This array holds the base of each memory pool. */ +static u8_t *const memp_bases[] = { +#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base, +#include "lwip/memp_std.h" +}; + +#else /* MEMP_SEPARATE_POOLS */ + +/** This is the actual memory used by the pools (all pools in one big block). */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#endif /* MEMP_SEPARATE_POOLS */ + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm". + */ +static int +memp_sanity(void) +{ + s16_t i; + struct memp *t, *h; + + for (i = 0; i < MEMP_MAX; i++) { + t = memp_tab[i]; + if(t != NULL) { + for (h = t->next; (t != NULL) && (h != NULL); t = t->next, + h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) { + if (t == h) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +#if defined(LWIP_DEBUG) && MEMP_STATS +static const char * memp_overflow_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) "/"desc, +#include "lwip/memp_std.h" + }; +#endif + +/** + * Check if a memp element was victim of an overflow + * (e.g. the restricted area after it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type]; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp overflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Check if a memp element was victim of an underflow + * (e.g. the restricted area before it has been altered) + * + * @param p the memp element to check + * @param memp_type the pool p comes from + */ +static void +memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type) +{ + u16_t k; + u8_t *m; +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) { + if (m[k] != 0xcd) { + char errstr[128] = "detected memp underflow in pool "; + char digit[] = "0"; + if(memp_type >= 10) { + digit[0] = '0' + (memp_type/10); + strcat(errstr, digit); + } + digit[0] = '0' + (memp_type%10); + strcat(errstr, digit); +#if defined(LWIP_DEBUG) && MEMP_STATS + strcat(errstr, memp_overflow_names[memp_type]); +#endif + LWIP_ASSERT(errstr, 0); + } + } +#endif +} + +/** + * Do an overflow check for all elements in every pool. + * + * @see memp_overflow_check_element for a description of the check + */ +static void +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_overflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { + memp_overflow_check_element_underflow(p, i); + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} + +/** + * Initialize the restricted areas of all memp elements in every pool. + */ +static void +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + for (j = 0; j < memp_num[i]; ++j) { +#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED; + memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED); +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_sizes[i]; + memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED); +#endif + p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED); + } + } +} +#endif /* MEMP_OVERFLOW_CHECK */ + +/** + * Initialize this module. + * + * Carves out memp_memory into linked lists for each pool-type. + */ +void +memp_init(void) +{ + struct memp *memp; + u16_t i, j; + + for (i = 0; i < MEMP_MAX; ++i) { + MEMP_STATS_AVAIL(used, i, 0); + MEMP_STATS_AVAIL(max, i, 0); + MEMP_STATS_AVAIL(err, i, 0); + MEMP_STATS_AVAIL(avail, i, memp_num[i]); + } + +#if !MEMP_SEPARATE_POOLS + memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; +#if MEMP_SEPARATE_POOLS + memp = (struct memp*)memp_bases[i]; +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i] +#if MEMP_OVERFLOW_CHECK + + MEMP_SANITY_REGION_AFTER_ALIGNED +#endif + ); + } + } +#if MEMP_OVERFLOW_CHECK + memp_overflow_init(); + /* check everything a first time to see if it worked */ + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK */ +} + +/** + * Get an element from a specific pool. + * + * @param type the pool to get an element from + * + * the debug version has two more parameters: + * @param file file name calling this function + * @param line number of line where this function is called + * + * @return a pointer to the allocated memory or a NULL pointer on error + */ +void * +#if !MEMP_OVERFLOW_CHECK +memp_malloc(memp_t type) +#else +memp_malloc_fn(memp_t type, const char* file, const int line) +#endif +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ + + memp = memp_tab[type]; + + if (memp != NULL) { + memp_tab[type] = memp->next; +#if MEMP_OVERFLOW_CHECK + memp->next = NULL; + memp->file = file; + memp->line = line; +#endif /* MEMP_OVERFLOW_CHECK */ + MEMP_STATS_INC_USED(used, type); + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); + memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE); + } else { + LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + MEMP_STATS_INC(err, type); + } + + SYS_ARCH_UNPROTECT(old_level); + + return memp; +} + +/** + * Put an element back into its pool. + * + * @param type the pool where to put mem + * @param mem the memp element to free + */ +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; + SYS_ARCH_DECL_PROTECT(old_level); + + if (mem == NULL) { + return; + } + LWIP_ASSERT("memp_free: mem properly aligned", + ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0); + + memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE); + + SYS_ARCH_PROTECT(old_level); +#if MEMP_OVERFLOW_CHECK +#if MEMP_OVERFLOW_CHECK >= 2 + memp_overflow_check_all(); +#else + memp_overflow_check_element_overflow(memp, type); + memp_overflow_check_element_underflow(memp, type); +#endif /* MEMP_OVERFLOW_CHECK >= 2 */ +#endif /* MEMP_OVERFLOW_CHECK */ + + MEMP_STATS_DEC(used, type); + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif /* MEMP_SANITY_CHECK */ + + SYS_ARCH_UNPROTECT(old_level); +} + +#endif /* MEMP_MEM_MALLOC */ diff --git a/external/badvpn_dns/lwip/src/core/netif.c b/external/badvpn_dns/lwip/src/core/netif.c new file mode 100644 index 00000000..4df1a90e --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/netif.c @@ -0,0 +1,918 @@ +/** + * @file + * lwIP network interface abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp_impl.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" +#if ENABLE_LOOPBACK +#include "lwip/sys.h" +#if LWIP_NETIF_LOOPBACK_MULTITHREADING +#include "lwip/tcpip.h" +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_AUTOIP +#include "lwip/autoip.h" +#endif /* LWIP_AUTOIP */ +#if LWIP_DHCP +#include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ + +#if LWIP_NETIF_STATUS_CALLBACK +#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0) +#else +#define NETIF_STATUS_CALLBACK(n) +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0) +#else +#define NETIF_LINK_CALLBACK(n) +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +static u8_t netif_num; + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +#if LWIP_HAVE_LOOPIF +static struct netif loop_netif; + +/** + * Initialize a lwip network interface structure for a loopback interface + * + * @param netif the lwip network interface structure for this loopif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + */ +static err_t +netif_loopif_init(struct netif *netif) +{ + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0); + + netif->name[0] = 'l'; + netif->name[1] = 'o'; + netif->output = netif_loop_output; + return ERR_OK; +} +#endif /* LWIP_HAVE_LOOPIF */ + +void +netif_init(void) +{ +#if LWIP_HAVE_LOOPIF + ip_addr_t loop_ipaddr, loop_netmask, loop_gw; + IP4_ADDR(&loop_gw, 127,0,0,1); + IP4_ADDR(&loop_ipaddr, 127,0,0,1); + IP4_ADDR(&loop_netmask, 255,0,0,0); + +#if NO_SYS + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input); +#else /* NO_SYS */ + netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input); +#endif /* NO_SYS */ + netif_set_up(&loop_netif); + +#endif /* LWIP_HAVE_LOOPIF */ +} + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input) +{ +#if LWIP_IPV6 + u32_t i; +#endif + + LWIP_ASSERT("No init function given", init != NULL); + + /* reset new interface configuration state */ + ip_addr_set_zero(&netif->ip_addr); + ip_addr_set_zero(&netif->netmask); + ip_addr_set_zero(&netif->gw); +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip6_addr_set_zero(&netif->ip6_addr[i]); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ + netif->flags = 0; +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /* netif not under AutoIP control by default */ + netif->autoip = NULL; +#endif /* LWIP_AUTOIP */ +#if LWIP_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /* netif not under DHCPv6 control by default */ + netif->dhcp6 = NULL; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_STATUS_CALLBACK + netif->status_callback = NULL; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + netif->link_callback = NULL; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_IGMP + netif->igmp_mac_filter = NULL; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if ENABLE_LOOPBACK + netif->loop_first = NULL; + netif->loop_last = NULL; +#endif /* ENABLE_LOOPBACK */ + + /* remember netif specific state information data */ + netif->state = state; + #ifdef PSIPHON + /* tun2socks as a library, with a multi-run lifetime, + may invoke this multiple times */ + netif->num = netif_num; +#else + netif->num = netif_num++; +#endif /* PSIPHON */ + netif->input = input; + NETIF_SET_HWADDRHINT(netif, NULL); +#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS + netif->loop_cnt_current = 0; +#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + snmp_inc_iflist(); + +#if LWIP_IGMP + /* start IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_start(netif); + } +#endif /* LWIP_IGMP */ + + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +/** + * Change IP address configuration for a network interface (including netmask + * and default gateway). + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * @param netmask the new netmask + * @param gw the new default gateway + */ +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +/** + * Remove a network interface from the list of lwIP netifs. + * + * @param netif the network interface to remove + */ +void +netif_remove(struct netif *netif) +{ + if (netif == NULL) { + return; + } + +#if LWIP_IGMP + /* stop IGMP processing */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_stop(netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + if (netif_is_up(netif)) { + /* set netif down before removing (call callback function) */ + netif_set_down(netif); + } + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + snmp_dec_iflist(); + /* this netif is default? */ + if (netif_default == netif) { + /* reset default netif */ + netif_set_default(NULL); + } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +/** + * Find a network interface by searching for its name + * + * @param name the name of the netif (like netif->name) plus concatenated number + * in ascii representation (e.g. 'en0') + */ +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +int netif_is_named (struct netif *netif, const char name[3]) +{ + u8_t num = name[2] - '0'; + + return (!memcmp(netif->name, name, 2) && netif->num == num); +} + +/** + * Change the IP address of a network interface + * + * @param netif the network interface to change + * @param ipaddr the new IP address + * + * @note call netif_set_addr() if you also want to change netmask and + * default gateway + */ +void +netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip)) +#endif /* LWIP_AUTOIP */ + ) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) && + (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr); + } + } + } +#endif + snmp_delete_ipaddridx_tree(netif); + snmp_delete_iprteidx_tree(0,netif); + /* set new IP address to netif */ + ip_addr_set(&(netif->ip_addr), ipaddr); + snmp_insert_ipaddridx_tree(netif); + snmp_insert_iprteidx_tree(0,netif); + + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->ip_addr), + ip4_addr2_16(&netif->ip_addr), + ip4_addr3_16(&netif->ip_addr), + ip4_addr4_16(&netif->ip_addr))); +} + +/** + * Change the default gateway for a network interface + * + * @param netif the network interface to change + * @param gw the new default gateway + * + * @note call netif_set_addr() if you also want to change ip address and netmask + */ +void +netif_set_gw(struct netif *netif, ip_addr_t *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->gw), + ip4_addr2_16(&netif->gw), + ip4_addr3_16(&netif->gw), + ip4_addr4_16(&netif->gw))); +} + +void netif_set_pretend_tcp (struct netif *netif, u8_t pretend) +{ + if (pretend) { + netif->flags |= NETIF_FLAG_PRETEND_TCP; + } else { + netif->flags &= ~NETIF_FLAG_PRETEND_TCP; + } +} + +/** + * Change the netmask of a network interface + * + * @param netif the network interface to change + * @param netmask the new netmask + * + * @note call netif_set_addr() if you also want to change ip address and + * default gateway + */ +void +netif_set_netmask(struct netif *netif, ip_addr_t *netmask) +{ + snmp_delete_iprteidx_tree(0, netif); + /* set new netmask to netif */ + ip_addr_set(&(netif->netmask), netmask); + snmp_insert_iprteidx_tree(0, netif); + LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1_16(&netif->netmask), + ip4_addr2_16(&netif->netmask), + ip4_addr3_16(&netif->netmask), + ip4_addr4_16(&netif->netmask))); +} + +/** + * Set a network interface as the default network interface + * (used to output all packets for which no specific route is found) + * + * @param netif the default network interface + */ +void +netif_set_default(struct netif *netif) +{ + if (netif == NULL) { + /* remove default route */ + snmp_delete_iprteidx_tree(1, netif); + } else { + /* install default route */ + snmp_insert_iprteidx_tree(1, netif); + } + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + if (!(netif->flags & NETIF_FLAG_UP)) { + netif->flags |= NETIF_FLAG_UP; + +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif /* LWIP_SNMP */ + + NETIF_STATUS_CALLBACK(netif); + + if (netif->flags & NETIF_FLAG_LINK_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & (NETIF_FLAG_ETHARP)) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + } + } +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + if (netif->flags & NETIF_FLAG_UP) { + netif->flags &= ~NETIF_FLAG_UP; +#if LWIP_SNMP + snmp_get_sysuptime(&netif->ts); +#endif + +#if LWIP_ARP + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_cleanup_netif(netif); + } +#endif /* LWIP_ARP */ + NETIF_STATUS_CALLBACK(netif); + } +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback) +{ + if (netif) { + netif->status_callback = status_callback; + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + if (!(netif->flags & NETIF_FLAG_LINK_UP)) { + netif->flags |= NETIF_FLAG_LINK_UP; + +#if LWIP_DHCP + if (netif->dhcp) { + dhcp_network_changed(netif); + } +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP + if (netif->autoip) { + autoip_network_changed(netif); + } +#endif /* LWIP_AUTOIP */ + + if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_ARP + /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ + if (netif->flags & NETIF_FLAG_ETHARP) { + etharp_gratuitous(netif); + } +#endif /* LWIP_ARP */ + +#if LWIP_IGMP + /* resend IGMP memberships */ + if (netif->flags & NETIF_FLAG_IGMP) { + igmp_report_groups( netif); + } +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + } + NETIF_LINK_CALLBACK(netif); + } +} + +/** + * Called by a driver when its link goes down + */ +void netif_set_link_down(struct netif *netif ) +{ + if (netif->flags & NETIF_FLAG_LINK_UP) { + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); + } +} + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback) +{ + if (netif) { + netif->link_callback = link_callback; + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if ENABLE_LOOPBACK +/** + * Send an IP packet to be received on the same netif (loopif-like). + * The pbuf is simply copied and handed back to netif->input. + * In multithreaded mode, this is done directly since netif->input must put + * the packet on a queue. + * In callback mode, the packet is put on an internal queue and is fed to + * netif->input by netif_poll(). + * + * @param netif the lwip network interface structure + * @param p the (IP) packet to 'send' + * @param ipaddr the ip address to send the packet to (not used) + * @return ERR_OK if the packet has been sent + * ERR_MEM if the pbuf used to copy the packet couldn't be allocated + */ +err_t +netif_loop_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + LWIP_UNUSED_ARG(ipaddr); + + /* Allocate a new pbuf */ + r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + if (r == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } +#if LWIP_LOOPBACK_MAX_PBUFS + clen = pbuf_clen(r); + /* check for overflow or too many pbuf on queue */ + if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || + ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return ERR_MEM; + } + netif->loop_cnt_current += clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + + /* Copy the whole pbuf queue p into the single pbuf r */ + if ((err = pbuf_copy(r, p)) != ERR_OK) { + pbuf_free(r); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(stats_if); + return err; + } + + /* Put the packet on a linked list which gets emptied through calling + netif_poll(). */ + + /* let last point to the last pbuf in chain r */ + for (last = r; last->next != NULL; last = last->next); + + SYS_ARCH_PROTECT(lev); + if(netif->loop_first != NULL) { + LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL); + netif->loop_last->next = r; + netif->loop_last = last; + } else { + netif->loop_first = r; + netif->loop_last = last; + } + SYS_ARCH_UNPROTECT(lev); + + LINK_STATS_INC(link.xmit); + snmp_add_ifoutoctets(stats_if, p->tot_len); + snmp_inc_ifoutucastpkts(stats_if); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((tcpip_callback_fn)netif_poll, netif); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +/** + * Call netif_poll() in the main loop of your application. This is to prevent + * reentering non-reentrant functions like tcp_input(). Packets passed to + * netif_loop_output() are put on a list that is passed to netif->input() by + * netif_poll(). + */ +void +netif_poll(struct netif *netif) +{ + struct pbuf *in; + /* If we have a loopif, SNMP counters are adjusted for it, + * if not they are adjusted for 'netif'. */ +#if LWIP_SNMP +#if LWIP_HAVE_LOOPIF + struct netif *stats_if = &loop_netif; +#else /* LWIP_HAVE_LOOPIF */ + struct netif *stats_if = netif; +#endif /* LWIP_HAVE_LOOPIF */ +#endif /* LWIP_SNMP */ + SYS_ARCH_DECL_PROTECT(lev); + + do { + /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ + SYS_ARCH_PROTECT(lev); + in = netif->loop_first; + if (in != NULL) { + struct pbuf *in_end = in; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = pbuf_clen(in); + /* adjust the number of pbufs on queue */ + LWIP_ASSERT("netif->loop_cnt_current underflow", + ((netif->loop_cnt_current - clen) < netif->loop_cnt_current)); + netif->loop_cnt_current -= clen; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + while (in_end->len != in_end->tot_len) { + LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL); + in_end = in_end->next; + } + /* 'in_end' now points to the last pbuf from 'in' */ + if (in_end == netif->loop_last) { + /* this was the last pbuf in the list */ + netif->loop_first = netif->loop_last = NULL; + } else { + /* pop the pbuf off the list */ + netif->loop_first = in_end->next; + LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL); + } + /* De-queue the pbuf from its successors on the 'loop_' list. */ + in_end->next = NULL; + } + SYS_ARCH_UNPROTECT(lev); + + if (in != NULL) { + LINK_STATS_INC(link.recv); + snmp_add_ifinoctets(stats_if, in->tot_len); + snmp_inc_ifinucastpkts(stats_if); + /* loopback packets are always IP packets! */ + if (ip_input(in, netif) != ERR_OK) { + pbuf_free(in); + } + /* Don't reference the packet any more! */ + in = NULL; + } + /* go on while there is a packet on the list */ + } while (netif->loop_first != NULL); +} + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +/** + * Calls netif_poll() for every netif on the netif_list. + */ +void +netif_poll_all(void) +{ + struct netif *netif = netif_list; + /* loop through netifs */ + while (netif != NULL) { + netif_poll(netif); + /* proceed to next network interface */ + netif = netif->next; + } +} +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +s8_t +netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +void +netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); + netif->ip6_addr[0].addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } + else { + /* Use hwaddr directly as interface ID. */ + netif->ip6_addr[0].addr[2] = 0; + netif->ip6_addr[0].addr[3] = 0; + + addr_index = 3; + for (i = 0; i < 8; i++) { + if (i == 4) { + addr_index--; + } + netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; +#else + /* Consider address valid. */ + netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + (void)netif; + (void)p; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/external/badvpn_dns/lwip/src/core/pbuf.c b/external/badvpn_dns/lwip/src/core/pbuf.c new file mode 100644 index 00000000..1e5e53b1 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/pbuf.c @@ -0,0 +1,1179 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE + * NOT SUPPORTED!!! Use helper structs to queue multiple packets. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" +#if LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) +/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically + aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ +#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) + +#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * Attempt to reclaim some memory from queued out-of-sequence TCP segments + * if we run out of pool pbufs. It's better to give priority to new packets + * if we're running out. + * + * This must be done in the correct thread context therefore this function + * can only be used with NO_SYS=0 and through tcpip_callback. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 0; + SYS_ARCH_UNPROTECT(old_level); + + for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { + if (NULL != pcb->ooseq) { + /** Free the ooseq pbufs of one PCB only */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + return; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (type) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { + PBUF_POOL_IS_EMPTY(); + return NULL; + } + p->type = type; + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", + (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); + if (q == NULL) { + PBUF_POOL_IS_EMPTY(); + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->type = type; + q->flags = 0; + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); + q->tot_len = (u16_t)rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); + q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", + ((u8_t*)p->payload + p->len <= + (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = (struct pbuf *)memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", + (type == PBUF_ROM) ? "ROM" : "REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->type = type; + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous type", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + /* set flags */ + p->flags = 0; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Initialize a custom pbuf (already allocated). + * + * @param layer flag to define header size + * @param length size of the pbuf's payload + * @param type type of the pbuf (only used to treat the pbuf accordingly, as + * this function allocates no memory) + * @param p pointer to the custom pbuf to initialize (already allocated) + * @param payload_mem pointer to the buffer that is used for payload and headers, + * must be at least big enough to hold 'length' plus the header size, + * may be NULL if set later. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least + * big enough to hold 'length' plus the header size + */ +struct pbuf* +pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, + void *payload_mem, u16_t payload_mem_len) +{ + u16_t offset; + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); + + /* determine header offset */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + break; + default: + LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); + return NULL; + } + + if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); + return NULL; + } + + p->pbuf.next = NULL; + if (payload_mem != NULL) { + p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); + } else { + p->pbuf.payload = NULL; + } + p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; + p->pbuf.len = p->pbuf.tot_len = length; + p->pbuf.type = type; + p->pbuf.ref = 1; + return &p->pbuf; +} +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s32_t grow; + + LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); + LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL || + p->type == PBUF_ROM || + p->type == PBUF_RAM || + p->type == PBUF_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + LWIP_ASSERT("grow < max_u16_t", grow < 0xffff); + q->tot_len += (u16_t)grow; + /* proceed to next pbuf in chain */ + q = q->next; + LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->type == PBUF_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len); + LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + u16_t type; + void *payload; + u16_t increment_magnitude; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) { + return 0; + } + + if (header_size_increment < 0){ + increment_magnitude = -header_size_increment; + /* Check that we aren't going to move off the end of the pbuf */ + LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); + } else { + increment_magnitude = header_size_increment; +#if 0 + /* Can't assert these as some callers speculatively call + pbuf_header() to see if it's OK. Will return 1 below instead. */ + /* Check that we've got the correct type of pbuf to work with */ + LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL", + p->type == PBUF_RAM || p->type == PBUF_POOL); + /* Check that we aren't going to move off the beginning of the pbuf */ + LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF", + (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF); +#endif + } + + type = p->type; + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (type == PBUF_RAM || type == PBUF_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, (void *)(p + 1))); + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (type == PBUF_REF || type == PBUF_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + return 1; + } + } else { + /* Unknown type */ + LWIP_ASSERT("bad pbuf type", 0); + return 1; + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param p The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + u16_t type; + struct pbuf *q; + u8_t count; + + if (p == NULL) { + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane type", + p->type == PBUF_RAM || p->type == PBUF_ROM || + p->type == PBUF_REF || p->type == PBUF_POOL); + + count = 0; + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + u16_t ref; + SYS_ARCH_DECL_PROTECT(old_level); + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. We put the new ref into a local variable to prevent + * further protection. */ + SYS_ARCH_PROTECT(old_level); + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + ref = --(p->ref); + SYS_ARCH_UNPROTECT(old_level); + /* this pbuf is no longer referenced to? */ + if (ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); + type = p->type; +#if LWIP_SUPPORT_CUSTOM_PBUF + /* is this a custom pbuf? */ + if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { + struct pbuf_custom *pc = (struct pbuf_custom*)p; + LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); + pc->custom_free_function(p); + } else +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + { + /* is this a pbuf from the pool? */ + if (type == PBUF_POOL) { + memp_free(MEMP_PBUF_POOL, p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (type == PBUF_ROM || type == PBUF_REF) { + memp_free(MEMP_PBUF, p); + /* type == PBUF_RAM */ + } else { + mem_free(p); + } + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); + /* stop walking through the chain */ + p = NULL; + } + } + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", + ((h != NULL) && (t != NULL)), return;); + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return ((tail_gone > 0) ? NULL : q); +} + +/** + * + * Create PBUF_RAM copies of pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * @note You MUST explicitly use p = pbuf_take(p); + * + * @note Only one packet is copied, no packet queue! + * + * @param p_to pbuf destination of the copy + * @param p_from pbuf source of the copy + * + * @return ERR_OK if pbuf was copied + * ERR_ARG if one of the pbufs is NULL or p_to is not big + * enough to hold p_from + */ +err_t +pbuf_copy(struct pbuf *p_to, struct pbuf *p_from) +{ + u16_t offset_to=0, offset_from=0, len; + + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", + (void*)p_to, (void*)p_from)); + + /* is the target big enough to hold the source? */ + LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && + (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); + + /* iterate through pbuf chain */ + do + { + /* copy one part of the original chain */ + if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { + /* complete current p_from fits into current p_to */ + len = p_from->len - offset_from; + } else { + /* current p_from does not fit into current p_to */ + len = p_to->len - offset_to; + } + MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len); + offset_to += len; + offset_from += len; + LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); + LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); + if (offset_from >= p_from->len) { + /* on to next p_from (if any) */ + offset_from = 0; + p_from = p_from->next; + } + if (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + if((p_from != NULL) && (p_from->len == p_from->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_from->next == NULL), return ERR_VAL;); + } + if((p_to != NULL) && (p_to->len == p_to->tot_len)) { + /* don't copy more than one packet! */ + LWIP_ERROR("pbuf_copy() does not allow packet queues!\n", + (p_to->next == NULL), return ERR_VAL;); + } + } while (p_from); + LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); + return ERR_OK; +} + +/** + * Copy (part of) the contents of a packet buffer + * to an application supplied buffer. + * + * @param buf the pbuf from which to copy data + * @param dataptr the application supplied buffer + * @param len length of data to copy (dataptr must be big enough). No more + * than buf->tot_len will be copied, irrespective of len + * @param offset offset into the packet buffer from where to begin copying len bytes + * @return the number of bytes copied, or 0 on failure + */ +u16_t +pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) +{ + struct pbuf *p; + u16_t left; + u16_t buf_copy_len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); + + left = 0; + + if((buf == NULL) || (dataptr == NULL)) { + return 0; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; len != 0 && p != NULL; p = p->next) { + if ((offset != 0) && (offset >= p->len)) { + /* don't copy from this buffer -> on to the next */ + offset -= p->len; + } else { + /* copy from this buffer. maybe only partially. */ + buf_copy_len = p->len - offset; + if (buf_copy_len > len) + buf_copy_len = len; + /* copy the necessary parts of the buffer */ + MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len); + copied_total += buf_copy_len; + left += buf_copy_len; + len -= buf_copy_len; + offset = 0; + } + } + return copied_total; +} + +/** + * Copy application supplied data into a pbuf. + * This function can only be used to copy the equivalent of buf->tot_len data. + * + * @param buf pbuf to fill with data + * @param dataptr application supplied data buffer + * @param len length of the application supplied data buffer + * + * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough + */ +err_t +pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) +{ + struct pbuf *p; + u16_t buf_copy_len; + u16_t total_copy_len = len; + u16_t copied_total = 0; + + LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;); + LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;); + + if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { + return ERR_ARG; + } + + /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ + for(p = buf; total_copy_len != 0; p = p->next) { + LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); + buf_copy_len = total_copy_len; + if (buf_copy_len > p->len) { + /* this pbuf cannot hold all remaining data */ + buf_copy_len = p->len; + } + /* copy the necessary parts of the buffer */ + MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len); + total_copy_len -= buf_copy_len; + copied_total += buf_copy_len; + } + LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); + return ERR_OK; +} + +/** + * Creates a single pbuf out of a queue of pbufs. + * + * @remark: Either the source pbuf 'p' is freed by this function or the original + * pbuf 'p' is returned, therefore the caller has to check the result! + * + * @param p the source pbuf + * @param layer pbuf_layer of the new pbuf + * + * @return a new, single pbuf (p->next is NULL) + * or the old pbuf if allocation fails + */ +struct pbuf* +pbuf_coalesce(struct pbuf *p, pbuf_layer layer) +{ + struct pbuf *q; + err_t err; + if (p->next == NULL) { + return p; + } + q = pbuf_alloc(layer, p->tot_len, PBUF_RAM); + if (q == NULL) { + /* @todo: what do we do now? */ + return p; + } + err = pbuf_copy(q, p); + LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); + pbuf_free(p); + return q; +} + +#if LWIP_CHECKSUM_ON_COPY +/** + * Copies data into a single pbuf (*not* into a pbuf queue!) and updates + * the checksum while copying + * + * @param p the pbuf to copy data into + * @param start_offset offset of p->payload where to copy the data to + * @param dataptr data to copy into the pbuf + * @param len length of data to copy into the pbuf + * @param chksum pointer to the checksum which is updated + * @return ERR_OK if successful, another error if the data does not fit + * within the (first) pbuf (no pbuf queues!) + */ +err_t +pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum) +{ + u32_t acc; + u16_t copy_chksum; + char *dst_ptr; + LWIP_ASSERT("p != NULL", p != NULL); + LWIP_ASSERT("dataptr != NULL", dataptr != NULL); + LWIP_ASSERT("chksum != NULL", chksum != NULL); + LWIP_ASSERT("len != 0", len != 0); + + if ((start_offset >= p->len) || (start_offset + len > p->len)) { + return ERR_ARG; + } + + dst_ptr = ((char*)p->payload) + start_offset; + copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); + if ((start_offset & 1) != 0) { + copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); + } + acc = *chksum; + acc += copy_chksum; + *chksum = FOLD_U32T(acc); + return ERR_OK; +} +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /** Get one byte from the specified position in a pbuf + * WARNING: returns zero for offset >= p->tot_len + * + * @param p pbuf to parse + * @param offset offset into p of the byte to return + * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len + */ +u8_t +pbuf_get_at(struct pbuf* p, u16_t offset) +{ + u16_t copy_from = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= copy_from)) { + copy_from -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > copy_from)) { + return ((u8_t*)q->payload)[copy_from]; + } + return 0; +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at wich to start comparing + * @param s2 buffer to compare + * @param n length of buffer to compare + * @return zero if equal, nonzero otherwise + * (0xffff if p is too short, diffoffset+1 otherwise) + */ +u16_t +pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n) +{ + u16_t start = offset; + struct pbuf* q = p; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= start)) { + start -= q->len; + q = q->next; + } + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > start)) { + u16_t i; + for(i = 0; i < n; i++) { + u8_t a = pbuf_get_at(q, start + i); + u8_t b = ((u8_t*)s2)[i]; + if (a != b) { + return i+1; + } + } + return 0; + } + return 0xffff; +} + +/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset + * start_offset. + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param mem search for the contents of this buffer + * @param mem_len length of 'mem' + * @param start_offset offset into p at which to start searching + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset) +{ + u16_t i; + u16_t max = p->tot_len - mem_len; + if (p->tot_len >= mem_len + start_offset) { + for(i = start_offset; i <= max; ) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } else { + i += plus; + } + } + } + return 0xFFFF; +} + +/** Find occurrence of substr with length substr_len in pbuf p, start at offset + * start_offset + * WARNING: in contrast to strstr(), this one does not stop at the first \0 in + * the pbuf/source string! + * + * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as + * return value 'not found' + * @param substr string to search for in p, maximum length is 0xFFFE + * @return 0xFFFF if substr was not found in p or the index where it was found + */ +u16_t +pbuf_strstr(struct pbuf* p, const char* substr) +{ + size_t substr_len; + if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { + return 0xFFFF; + } + substr_len = strlen(substr); + if (substr_len >= 0xFFFF) { + return 0xFFFF; + } + return pbuf_memfind(p, substr, (u16_t)substr_len, 0); +} diff --git a/external/badvpn_dns/lwip/src/core/raw.c b/external/badvpn_dns/lwip/src/core/raw.c new file mode 100644 index 00000000..68b23c61 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/raw.c @@ -0,0 +1,422 @@ +/** + * @file + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#include + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs; + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param p pbuf to be demultiplexed to a RAW PCB. + * @param inp network interface on which the datagram was received. + * @return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb, *prev; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; +#if LWIP_IPV6 + struct ip6_hdr *ip6hdr; +#endif /* LWIP_IPV6 */ + + + LWIP_UNUSED_ARG(inp); + + iphdr = (struct ip_hdr *)p->payload; +#if LWIP_IPV6 + if (IPH_V(iphdr) == 6) { + ip6hdr = (struct ip6_hdr *)p->payload; + proto = IP6H_NEXTH(ip6hdr); + } + else +#endif /* LWIP_IPV6 */ + { + proto = IPH_PROTO(iphdr); + } + + prev = NULL; + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) +#if LWIP_IPV6 + && !PCB_ISIPV6(pcb) +#endif /* LWIP_IPV6 */ + ) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv.ip4 != NULL) { +#ifndef LWIP_NOASSERT + void* old_payload = p->payload; +#endif + /* the receive callback function did not eat the packet? */ + eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); + if (eaten != 0) { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + if (prev != NULL) { + /* move the pcb to the front of raw_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + } else { + /* sanity-check that the receive callback did not alter the pbuf */ + LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", + p->payload == old_payload); + } + } + /* no receive callback function was set for this raw PCB */ + } + /* drop the packet */ + } + prev = pcb; + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t +raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + ipX_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + s16_t header_size; + ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + header_size = ( +#if LWIP_IPV6 + PCB_ISIPV6(pcb) ? IP6_HLEN : +#endif /* LWIP_IPV6 */ + IP_HLEN); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, header_size)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + } + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + if(pbuf_header(q, -header_size)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); + if (netif == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST +#if LWIP_IPV6 + /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */ + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_VAL; + } + } +#endif /* IP_SOF_BROADCAST */ + + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); +#if LWIP_IPV6 + if (src_ip == NULL) { + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } +#endif /* LWIP_IPV6 */ + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &pcb->local_ip; + } + + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * + */ +err_t +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip)); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else { + for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new(u8_t proto) +{ + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#if LWIP_IPV6 +/** + * Create a RAW PCB for IPv6. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number (next header) of the IPv6 packet payload + * (e.g. IP6_NEXTH_ICMP6) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new_ip6(u8_t proto) +{ + struct raw_pcb *pcb; + pcb = raw_new(proto); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_RAW */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/asn1_dec.c b/external/badvpn_dns/lwip/src/core/snmp/asn1_dec.c new file mode 100644 index 00000000..1d565820 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/asn1_enc.c b/external/badvpn_dns/lwip/src/core/snmp/asn1_enc.c new file mode 100644 index 00000000..64dfc5f6 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = (u8_t)length; + } + else + { + /* most significant length octet */ + *msg_ptr = (u8_t)(length >> 8); + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]); + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/mib2.c b/external/badvpn_dns/lwip/src/core/snmp/mib2.c new file mode 100644 index 00000000..dcd3b62c --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/mib2.c @@ -0,0 +1,4146 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "lwip/sys.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10)) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node*)&udp_root, (struct mib_node*)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node*)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 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 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root, + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable, + (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable, + (struct mib_node*)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node*)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node*)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node*)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node*)&sys_tem, + (struct mib_node*)&interfaces, + (struct mib_node*)&at, + (struct mib_node*)&mib2_ip, + (struct mib_node*)&icmp, +#if LWIP_TCP + (struct mib_node*)&tcp, +#endif + (struct mib_node*)&udp, + (struct mib_node*)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n) +{ + u16_t i = n; + while (i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + u8_t i = n; + while(i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + insert = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + insert = 1; + } + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t del = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + del = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + del = 1; + } + } + if (del) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct udp_pcb *npcb; + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + npcb = udp_pcbs; + while ((npcb != NULL)) + { + if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) && + (npcb->local_port == udpidx[4])) + { + bindings++; + } + npcb = npcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy((u8_t*)value, sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t))); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime((u32_t*)value); + } + break; + case 4: /* sysContact */ + ocstrncpy((u8_t*)value, syscontact_ptr, len); + break; + case 5: /* sysName */ + ocstrncpy((u8_t*)value, sysname_ptr, len); + break; + case 6: /* sysLocation */ + ocstrncpy((u8_t*)value, syslocation_ptr, len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid len", len <= 0xff); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr, (u8_t*)value, len); + *syscontact_len_ptr = (u8_t)len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr, (u8_t*)value, len); + *sysname_len_ptr = (u8_t)len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr, (u8_t*)value, len); + *syslocation_len_ptr = (u8_t)len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy((u8_t*)value, (u8_t*)netif->name, len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy((u8_t*)value, netif->hwaddr, len); + break; + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t))); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + LWIP_UNUSED_ARG(len); + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + LWIP_UNUSED_ARG(len); + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + ip_addr_t ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = (s32_t*)value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = IPADDR_BROADCAST & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + ip_addr_t dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + + if (ip_addr_isany(&dest)) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has 0.0.0.0 dest */ + ip_addr_set_zero(dst); + } + else + { + /* netifs have netaddress dest */ + ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask); + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = (s32_t*)value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = (s32_t*)value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = (s32_t*)value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte use 0.0.0.0 mask */ + ip_addr_set_zero(dst); + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t))); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = (s32_t*)value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + ip_addr_t lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + ipX_addr_t ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip); + LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff)); + port = (u16_t)od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !(ipX_addr_cmp(0, &pcb->local_ip, &ip) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + ipX_addr_t *dst = (ipX_addr_t*)value; + ipX_addr_copy(0, *dst, pcb->local_ip); + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */ + u8_t *ptr = (u8_t*)value; + *snmpenableauthentraps_ptr = *ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/mib_structs.c b/external/badvpn_dns/lwip/src/core/snmp/mib_structs.c new file mode 100644 index 00000000..2f185cb4 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/mib_structs.c @@ -0,0 +1,1174 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/memp.h" +#include "lwip/netif.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + s32_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, ip_addr_t *ip) +{ + IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]); +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(ip_addr_t *ip, s32_t *ident) +{ + ident[0] = ip4_addr1(ip); + ident[1] = ip4_addr2(ip); + ident[2] = ip4_addr3(ip); + ident[3] = ip4_addr4(ip); +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + memp_free(MEMP_SNMP_NODE, ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + memp_free(MEMP_SNMP_ROOTNODE, lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (return) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/msg_in.c b/external/badvpn_dns/lwip/src/core/snmp/msg_in.c new file mode 100644 index 00000000..be940c62 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/msg_in.c @@ -0,0 +1,1453 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/memp.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + +#ifdef SNMP_PRIVATE_MIB_INIT + /* If defined, this must be a function-like define to initialize the + * private MIB after the stack has been initialized. + * The private MIB can also be initialized in tcpip_callback (or after + * the stack is initialized), this define is only for convenience. */ + SNMP_PRIVATE_MIB_INIT(); +#endif /* SNMP_PRIVATE_MIB_INIT */ + + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + /* move names back from outvb to invb */ + int v; + struct snmp_varbind *vbi = msg_ps->invb.head; + struct snmp_varbind *vbo = msg_ps->outvb.head; + for (v=0; vvb_idx; v++) { + vbi->ident_len = vbo->ident_len; + vbo->ident_len = 0; + vbi->ident = vbo->ident; + vbo->ident = NULL; + vbi = vbi->next; + vbo = vbo->next; + } + /* free outvb */ + snmp_varbind_list_free(&msg_ps->outvb); + /* we send invb back */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + /* error index must be 0 for error too big */ + msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) && + (msg_ps->ext_object_def.access & MIB_ACCESS_READ)) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb->value_len = (u8_t)msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if ((object_def.instance != MIB_OBJECT_NONE) && + (object_def.access & MIB_ACCESS_READ)) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb->value_len = (u8_t)object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", + vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + vb->ident = NULL; + vb->ident_len = 0; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + (u8_t)msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access & MIB_ACCESS_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + err_t err_ret; + u16_t payload_len = p->tot_len; + u16_t payload_ofs = 0; + u16_t varbind_ofs = 0; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx == SNMP_CONCURRENT_REQUESTS) + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + return; + } + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + /* Only accept requests and requests without error (be robust) */ + /* Reject response and trap headers or error requests as input! */ + if ((err_ret != ERR_OK) || + ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) || + ((msg_ps->error_status != SNMP_ES_NOERROR) || + (msg_ps->error_index != 0)) ) + { + /* header check failed drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + return; + } + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0)) + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + return; + } + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = (u8_t)len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = (s32_t*)vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = (u32_t*)vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + LWIP_ASSERT("invalid length", len <= 0xff); + vb = snmp_varbind_alloc(&oid, type, (u8_t)len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = (s32_t*)vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH); + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE); + if (vb->ident == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + /* allocate raw bytes for our object value */ + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); + if (vb->ident != NULL) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->value); + } + if (vb->ident != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/snmp/msg_out.c b/external/badvpn_dns/lwip/src/core/snmp/msg_out.c new file mode 100644 index 00000000..fc0807c5 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/snmp/msg_out.c @@ -0,0 +1,678 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + ip_addr_t dst_ip; + struct pbuf *p; + u16_t i,tot_len; + err_t err = ERR_OK; + + for (i=0, td = &trap_dst[0]; ienable != 0) && !ip_addr_isany(&td->dip)) + { + /* network order trap destination */ + ip_addr_copy(trap_msg.dip, td->dip); + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + if (dst_if != NULL) { + ip_addr_copy(dst_ip, dst_if->ip_addr); + /* @todo: what about IPv6? */ + trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); + trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); + trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); + trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** send to the TRAP destination */ + udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; + } + } + } + return err; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = (u8_t*)vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/external/badvpn_dns/lwip/src/core/stats.c b/external/badvpn_dns/lwip/src/core/stats.c new file mode 100644 index 00000000..06fbe0f2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/stats.c @@ -0,0 +1,181 @@ +/** + * @file + * Statistics module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" + +#include + +struct stats_ lwip_stats; + +void stats_init(void) +{ +#ifdef LWIP_DEBUG +#if MEMP_STATS + const char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + int i; + for (i = 0; i < MEMP_MAX; i++) { + lwip_stats.memp[i].name = memp_names[i]; + } +#endif /* MEMP_STATS */ +#if MEM_STATS + lwip_stats.mem.name = "MEM"; +#endif /* MEM_STATS */ +#endif /* LWIP_DEBUG */ +} + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit)); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit)); + LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv)); + LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr)); + LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr)); + LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1)); + LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general)); + LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report)); + LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join)); + LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave)); + LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report)); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, const char *name) +{ + LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used)); + LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max)); + LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err)); +} + +#if MEMP_STATS +void +stats_display_memp(struct stats_mem *mem, int index) +{ + char * memp_names[] = { +#define LWIP_MEMPOOL(name,num,size,desc) desc, +#include "lwip/memp_std.h" + }; + if(index < MEMP_MAX) { + stats_display_mem(mem, memp_names[index]); + } +} +#endif /* MEMP_STATS */ +#endif /* MEM_STATS || MEMP_STATS */ + +#if SYS_STATS +void +stats_display_sys(struct stats_sys *sys) +{ + LWIP_PLATFORM_DIAG(("\nSYS\n\t")); + LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used)); + LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max)); + LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err)); + LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used)); + LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max)); + LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err)); + LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used)); + LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max)); + LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err)); +} +#endif /* SYS_STATS */ + +void +stats_display(void) +{ + s16_t i; + + LINK_STATS_DISPLAY(); + ETHARP_STATS_DISPLAY(); + IPFRAG_STATS_DISPLAY(); + IP6_FRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + ICMP6_STATS_DISPLAY(); + UDP_STATS_DISPLAY(); + TCP_STATS_DISPLAY(); + MEM_STATS_DISPLAY(); + for (i = 0; i < MEMP_MAX; i++) { + MEMP_STATS_DISPLAY(i); + } + SYS_STATS_DISPLAY(); +} +#endif /* LWIP_STATS_DISPLAY */ + +#endif /* LWIP_STATS */ + diff --git a/external/badvpn_dns/lwip/src/core/sys.c b/external/badvpn_dns/lwip/src/core/sys.c new file mode 100644 index 00000000..f1777372 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/sys.c @@ -0,0 +1,68 @@ +/** + * @file + * lwIP Operating System abstraction + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/sys.h" + +/* Most of the functions defined in sys.h must be implemented in the + * architecture-dependent file sys_arch.c */ + +#if !NO_SYS + +#ifndef sys_msleep +/** + * Sleep for some ms. Timeouts are NOT processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + if (ms > 0) { + sys_sem_t delaysem; + err_t err = sys_sem_new(&delaysem, 0); + if (err == ERR_OK) { + sys_arch_sem_wait(&delaysem, ms); + sys_sem_free(&delaysem); + } + } +} +#endif /* sys_msleep */ + +#endif /* !NO_SYS */ diff --git a/external/badvpn_dns/lwip/src/core/tcp.c b/external/badvpn_dns/lwip/src/core/tcp.c new file mode 100644 index 00000000..55496d05 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/tcp.c @@ -0,0 +1,1852 @@ +/** + * @file + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/snmp.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/debug.h" +#include "lwip/stats.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" + +#include + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +const char * const tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* Incremented every coarse grained timer shot (typically every 500 ms). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + /* Times per slowtmr hits */ +const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 }; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs bound but not yet (connected || listening) */ +struct tcp_pcb *tcp_bound_pcbs; +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + +/** Only used for temporary storage. */ +struct tcp_pcb *tcp_tmp_pcb; + +u8_t tcp_active_pcbs_changed; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Called periodically to dispatch TCP timers. + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the TX side of a connection held by the PCB. + * For tcp_close(), a RST is sent if the application didn't receive all data + * (tcp_recved() not called for all data passed to recv callback). + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it. + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +static err_t +tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data) +{ + err_t err; + + if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + /* Not all data received by application, send RST to tell the remote + side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, pcb); + } + return ERR_OK; + } + } + + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + err = ERR_OK; + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + /* To ensure all data has been sent when tcp_close returns, we have + to make sure tcp_output doesn't fail. + Since we don't really have to ensure all data has been sent when tcp_close + returns (unsent data is sent from tcp timer functions, also), we don't care + for the return value of tcp_output for now. */ + /* @todo: When implementing SO_LINGER, this must be changed somehow: + If SOF_LINGER is set, the data should be sent and acked before close returns. + This can only be valid for sequential APIs, not for the raw API. */ + tcp_output(pcb); + } + return err; +} + +/** + * Closes the connection held by the PCB. + * + * Listening pcbs are freed and may not be referenced any more. + * Connection pcbs are freed if not yet connected and may not be referenced + * any more. If a connection is established (at least SYN received or in + * a closing state), the connection is closed, and put in a closing state. + * The pcb is then automatically freed in tcp_slowtmr(). It is therefore + * unsafe to reference it (unless an error is returned). + * + * @param pcb the tcp_pcb to close + * @return ERR_OK if connection has been closed + * another err_t if closing failed and pcb is not freed + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + if (pcb->state != LISTEN) { + /* Set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + } + /* ... and close */ + return tcp_close_shutdown(pcb, 1); +} + +/** + * Causes all or part of a full-duplex connection of this PCB to be shut down. + * This doesn't deallocate the PCB unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. + * + * @param pcb PCB to shutdown + * @param shut_rx shut down receive side if this is != 0 + * @param shut_tx shut down send side if this is != 0 + * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down) + * another err_t on error. + */ +err_t +tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx) +{ + if (pcb->state == LISTEN) { + return ERR_CONN; + } + if (shut_rx) { + /* shut down the receive side: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + if (shut_tx) { + /* This can't happen twice since if it succeeds, the pcb's state is changed. + Only close in these states as the others directly deallocate the PCB */ + switch (pcb->state) { + case SYN_RCVD: + case ESTABLISHED: + case CLOSE_WAIT: + return tcp_close_shutdown(pcb, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + return ERR_OK; +} + +/** + * Abandons a connection and optionally sends a RST to the remote + * host. Deletes the local protocol control block. This is done when + * a connection is killed because of shortage of memory. + * + * @param pcb the tcp_pcb to abort + * @param reset boolean to indicate whether a reset should be sent + */ +void +tcp_abandon(struct tcp_pcb *pcb, int reset) +{ + u32_t seqno, ackno; +#if LWIP_CALLBACK_API + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + int send_rst = reset && (pcb->state != CLOSED); + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + TCP_PCB_REMOVE_ACTIVE(pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + if (send_rst) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + } +} + +/** + * Aborts the connection by sending a RST (reset) segment to the remote + * host. The pcb is deallocated. This function never fails. + * + * ATTENTION: When calling this from one of the TCP callbacks, make + * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise + * or you will risk accessing deallocated memory or memory leaks! + * + * @param pcb the tcp pcb to abort + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + tcp_abandon(pcb, 1); +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + * @param pcb the tcp_pcb to bind (no check is done whether this pcb is + * already bound!) + * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind + * to any local address + * @param port the local port to bind to + * @return ERR_USE if the port is already in use + * ERR_VAL if bind failed because the PCB is not in a valid state + * ERR_OK if bound + */ +err_t +tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL); + +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ + if (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* @todo: check accept_any_ip_version */ + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || + ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { + return ERR_USE; + } + } + } + } + } + + pcb->bound_to_netif = 0; + if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr)); + } + pcb->local_port = port; + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} + +err_t +tcp_bind_to_netif(struct tcp_pcb *pcb, const char ifname[3]) +{ + LWIP_ERROR("tcp_bind_if: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + /* Check if the interface is already in use */ + for (int i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(struct tcp_pcb *cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + cpcb->bound_to_netif && + !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif))) { + return ERR_USE; + } + } + } + + pcb->bound_to_netif = 1; + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->local_ip); + pcb->local_port = 0; + memcpy(pcb->local_netif, ifname, sizeof(pcb->local_netif)); + TCP_REG(&tcp_bound_pcbs, pcb); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind_to_netif: bind to interface %c%c%c\n", ifname[0], ifname[1], ifname[2])); + return ERR_OK; +} + +#if LWIP_CALLBACK_API +/** + * Default accept callback if no accept callback is specified by the user. + */ +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(err); + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + * @param pcb the original tcp_pcb + * @param backlog the incoming connections queue limit + * @return tcp_pcb used for listening, consumes less memory. + * + * @note The original tcp_pcb is freed. This function therefore has to be + * called like this: + * tpcb = tcp_listen(tpcb); + */ +struct tcp_pcb * +tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb_listen *lpcb; + + LWIP_UNUSED_ARG(backlog); + LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL); + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR) && !pcb->have_local_netif) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb)) { + if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ + lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->bound_to_netif = pcb->bound_to_netif; + lpcb->local_port = pcb->local_port; + memcpy(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + lpcb->state = LISTEN; + lpcb->prio = pcb->prio; + lpcb->so_options = pcb->so_options; + ip_set_option(lpcb, SOF_ACCEPTCONN); + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; +#if LWIP_IPV6 + PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); + lpcb->accept_any_ip_version = 0; +#endif /* LWIP_IPV6 */ + ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip); + if (pcb->local_port != 0 || pcb->bound_to_netif) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ +#if TCP_LISTEN_BACKLOG + lpcb->accepts_pending = 0; + lpcb->backlog = (backlog ? backlog : 1); +#endif /* TCP_LISTEN_BACKLOG */ + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb); + return (struct tcp_pcb *)lpcb; +} + +#if LWIP_IPV6 +/** + * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 + * connections, if the pcb's local address is set to ANY. + */ +struct tcp_pcb * +tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb *lpcb; + + lpcb = tcp_listen_with_backlog(pcb, backlog); + if ((lpcb != NULL) && + ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* The default behavior is to accept connections on either + * IPv4 or IPv6, if not bound. */ + /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ + ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; + } + return lpcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Update the state that tracks the available window space to advertise. + * + * Returns how much extra window would be advertised if we sent an + * update now. + */ +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb) +{ + u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd; + + if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) { + /* we can advertise more window */ + pcb->rcv_ann_wnd = pcb->rcv_wnd; + return new_right_edge - pcb->rcv_ann_right_edge; + } else { + if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) { + /* Can happen due to other end sending out of advertised window, + * but within actual available (but not yet advertised) window */ + pcb->rcv_ann_wnd = 0; + } else { + /* keep the right edge of window constant */ + u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); + pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd; + } + return 0; + } +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + * @param pcb the tcp_pcb for which data is read + * @param len the amount of bytes that have been read by the application + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + int wnd_inflation; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n", + len <= 0xffff - pcb->rcv_wnd ); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } + + wnd_inflation = tcp_update_rcv_ann_wnd(pcb); + + /* If the change in the right edge of window is significant (default + * watermark is TCP_WND/4), then send an explicit update now. + * Otherwise wait for a packet to be sent in the normal course of + * events (or more window to be available later) */ + if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) { + tcp_ack_now(pcb); + tcp_output(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_port = TCP_LOCAL_PORT_RANGE_START; + } + /* Check all PCB lists. */ + for (i = 0; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (or on error) + * @return ERR_VAL if invalid arguments are given + * ERR_OK if connect request has been sent + * other err_t values if connect request couldn't be sent + */ +err_t +tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, + tcp_connected_fn connected) +{ + err_t ret; + u32_t iss; + u16_t old_local_port; + + LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + LWIP_ERROR("tcp_connect: cannot connect pcb bound to netif", !pcb->bound_to_netif, return ERR_VAL); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); + } else { + return ERR_VAL; + } + pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* no local IP address set, yet. */ + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the address as local address of the pcb. */ + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ + for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + IP_PCB_IPVER_EQ(cpcb, pcb) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->snd_wnd = TCP_WND; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); +#endif /* LWIP_CALLBACK_API */ + + /* Send a SYN together with the MSS option. */ + ret = tcp_enqueue_flags(pcb, TCP_SYN); + if (ret == ERR_OK) { + /* SYN segment was enqueued, changed the pcbs state now */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(pcb); + snmp_inc_tcpactiveopens(); + + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *prev; + u16_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + u8_t pcb_reset; /* flag if a RST should be sent when removing */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + pcb_remove = 0; + pcb_reset = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + if (pcb->persist_backoff > 0) { + /* If snd_wnd is zero, use persist timer to send 1 byte probes + * instead of using the standard retransmission mechanism. */ + pcb->persist_cnt++; + if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { + pcb->persist_cnt = 0; + if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { + pcb->persist_backoff++; + } + tcp_zero_window_probe(pcb); + } + } else { + /* Increase the retransmission timer if it is running */ + if(pcb->rtime >= 0) { + ++pcb->rtime; + } + + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F + " pcb->rto %"S16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + + /* Reset the retransmission timer. */ + pcb->rtime = 0; + + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < (pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F + " ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one + mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + } + + /* Check if KEEPALIVE should be sent */ + if(ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + ++pcb_remove; + ++pcb_reset; + } + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + tcp_keepalive(pcb); + pcb->keep_cnt_sent++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_err_fn err_fn; + void *err_arg; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } else { + /* get the 'next' element now and work with 'prev' below (in case of abort) */ + prev = pcb; + pcb = pcb->next; + + /* We check if we should poll the connection. */ + ++prev->polltmr; + if (prev->polltmr >= prev->pollinterval) { + prev->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + tcp_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* if err == ERR_ABRT, 'prev' is already deallocated */ + if (err == ERR_OK) { + tcp_output(prev); + } + } + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + struct tcp_pcb *pcb2; + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously + * "refused" by upper layer (application) and sends delayed ACKs. + * + * Automatically called from tcp_tmr(). + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + ++tcp_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while(pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + tcp_output(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; + pcb->refused_data = NULL; + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN) { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* if err == ERR_ABRT, 'pcb' is already deallocated */ + /* Drop incoming packets because pcb is "full" (only if the incoming + segment contains data). */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ + pcb->refused_data = refused_data; + } + return ERR_OK; +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + */ +void +tcp_segs_free(struct tcp_seg *seg) +{ + while (seg != NULL) { + struct tcp_seg *next = seg->next; + tcp_seg_free(seg); + seg = next; + } +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + */ +void +tcp_seg_free(struct tcp_seg *seg) +{ + if (seg != NULL) { + if (seg->p != NULL) { + pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } +} + +/** + * Sets the priority of a connection. + * + * @param pcb the tcp_pcb to manipulate + * @param prio new priority + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} + +#if TCP_QUEUE_OOSEQ +/** + * Returns a copy of the given TCP segment. + * The pbuf and data are not copied, only the pointers + * + * @param seg the old tcp_seg + * @return a copy of seg + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif /* TCP_QUEUE_OOSEQ */ + +#if LWIP_CALLBACK_API +/** + * Default receive callback that is called if the user didn't register + * a recv callback for the pcb. + */ +err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + LWIP_UNUSED_ARG(arg); + if (p != NULL) { + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Kills the oldest active connection that has the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Kills the oldest connection that is in TIME_WAIT state. + * Called from tcp_alloc() if no more connections are available. + */ +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + +/** + * Allocate a new tcp_pcb structure. + * + * @param prio priority for the new pcb + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing active connections with lower priority than the new one. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio)); + tcp_kill_prio(prio); + /* Try to allocate a tcp_pcb again. */ + pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB); + if (pcb != NULL) { + /* adjust err stats: memp_malloc failed twice before */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + /* adjust err stats: timewait PCB was freed above */ + MEMP_STATS_DEC(err, MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; + /* As initial send MSS, we use TCP_MSS but limit it to 536. + The send MSS is updated when an MSS option is received. */ + pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = -1; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + pcb->last_timer = tcp_timer_ctr; + + pcb->polltmr = 0; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keep_idle = TCP_KEEPIDLE_DEFAULT; + +#if LWIP_TCP_KEEPALIVE + pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT; + pcb->keep_cnt = TCP_KEEPCNT_DEFAULT; +#endif /* LWIP_TCP_KEEPALIVE */ + + pcb->keep_cnt_sent = 0; + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. Port reservation using tcp_bind() is implemented but + * allocated pcbs that are not bound can't be killed automatically if wanting + * to allocate a pcb with higher prio (@see tcp_kill_prio()) + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +#if LWIP_IPV6 +/** + * Creates a new TCP-over-IPv6 protocol control block but doesn't + * place it on any of the TCP PCB lists. + * The pcb is not put on any list until binding using tcp_bind(). + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip6(void) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Used to specify the argument that should be passed callback + * functions. + * + * @param pcb tcp_pcb to set the callback argument + * @param arg void pointer argument to pass to callback functions + */ +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @param pcb tcp_pcb to set the recv callback + * @param recv callback function to call for this pcb when data is received + */ +void +tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv) +{ + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @param pcb tcp_pcb to set the sent callback + * @param sent callback function to call for this pcb when data is successfully sent + */ +void +tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent) +{ + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @param pcb tcp_pcb to set the accept callback + * @param accept callback function to call for this pcb when LISTENing + * connection has been connected to another host + */ +void +tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#if LWIP_CALLBACK_API + pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory + * (pcb->ooseq, pcb->unsent and pcb->unacked are freed). + * + * @param pcb tcp_pcb to purge. The pcb itself is not deallocated! + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + +#if TCP_LISTEN_BACKLOG + if (pcb->state == SYN_RCVD) { + /* Need to find the corresponding listen_pcb and decrease its accepts_pending */ + struct tcp_pcb_listen *lpcb; + LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL", + tcp_listen_pcbs.listen_pcbs != NULL); + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((!lpcb->bound_to_netif && !pcb->bound_to_netif && + (lpcb->local_port == pcb->local_port) && + IP_PCB_IPVER_EQ(pcb, lpcb) && + (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) || + (lpcb->bound_to_netif && pcb->bound_to_netif && + !memcmp(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)))) { + /* port and address of the listen pcb match the timed-out pcb */ + LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending", + lpcb->accepts_pending > 0); + lpcb->accepts_pending--; + break; + } + } + } +#endif /* TCP_LISTEN_BACKLOG */ + + + if (pcb->refused_data != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n")); + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; +#if TCP_OVERSIZE + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + * @param pcblist PCB list to purge. + * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated! + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + + if (pcb->state != LISTEN) { + LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL); + LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL); +#if TCP_QUEUE_OOSEQ + LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL); +#endif /* TCP_QUEUE_OOSEQ */ + } + + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + * @return u32_t pseudo random sequence number + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_CALCULATE_EFF_SEND_MSS +/** + * Calcluates the effective send mss that can be used for a specific IP address + * by using ip_route to determin the netif used to send to the address and + * calculating the minimum of TCP_MSS and that netif's mtu (if set). + */ +u16_t +tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + outif = ipX_route(isipv6, src, dest); +#if LWIP_IPV6 + if (isipv6) { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); + } else +#endif /* LWIP_IPV6 */ + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } + + if (mtu != 0) { + mss_s = mtu - IP_HLEN - TCP_HLEN; +#if LWIP_IPV6 + /* for IPv6, substract the difference in header size */ + mss_s -= (IP6_HLEN - IP_HLEN); +#endif /* LWIP_IPV6 */ + /* RFC 1122, chap 4.2.2.6: + * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize + * We correct for TCP options in tcp_write(), and don't support IP options. + */ + sendmss = LWIP_MIN(sendmss, mss_s); + } + return sendmss; +} +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +const char* +tcp_debug_state_str(enum tcp_state s) +{ + return tcp_state_str[s]; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +/** + * Print a tcp header for debugging purposes. + * + * @param tcphdr pointer to a struct tcp_hdr + */ +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +/** + * Print a tcp state for debugging purposes. + * + * @param s enum tcp_state to print + */ +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s])); +} + +/** + * Print tcp flags for debugging purposes. + * + * @param flags tcp flags, all active flags are printed + */ +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +} + +/** + * Print all tcp_pcbs in every list for debugging purposes. + */ +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +/** + * Check state consistency of the tcp_pcb lists. + */ +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ + +#endif /* LWIP_TCP */ diff --git a/external/badvpn_dns/lwip/src/core/tcp_in.c b/external/badvpn_dns/lwip/src/core/tcp_in.c new file mode 100644 index 00000000..92ee2b2a --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/tcp_in.c @@ -0,0 +1,1666 @@ +/** + * @file + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of the TCP layer. + * + * These functions are generally called in the order (ip_input() ->) + * tcp_input() -> * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + * + * @param p received TCP segment to process (p->payload pointing to the TCP header) + * @param inp network interface on which this segment was received + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; +#if SO_REUSE + struct tcp_pcb *lpcb_prev = NULL; + struct tcp_pcb_listen *lpcb_any = NULL; +#endif /* SO_REUSE */ + u8_t hdrlen; + err_t err; +#if CHECKSUM_CHECK_TCP + u16_t chksum; +#endif /* CHECKSUM_CHECK_TCP */ + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + tcphdr = (struct tcp_hdr *)p->payload; + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* Check that TCP header fits in payload */ + if (p->len < sizeof(struct tcp_hdr)) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || + ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { + TCP_STATS_INC(tcp.proterr); + goto dropped; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, + ipX_current_src_addr(), ipX_current_dest_addr()); + if (chksum != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + chksum)); + tcp_debug_print(tcphdr); + TCP_STATS_INC(tcp.chkerr); + goto dropped; + } +#endif /* CHECKSUM_CHECK_TCP */ + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + IP_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + struct tcp_pcb_listen *netif_pcb = NULL; + struct tcp_pcb *netif_pcb_prev; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->bound_to_netif) { + if (IP_PCB_IPVER_INPUT_MATCH(lpcb) && netif_is_named(inp, lpcb->local_netif)) { + netif_pcb = lpcb; + netif_pcb_prev = prev; + } + } + else if (lpcb->local_port == tcphdr->dest) { +#if LWIP_IPV6 + if (lpcb->accept_any_ip_version) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; +#endif /* SO_REUSE */ + } else +#endif /* LWIP_IPV6 */ + if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { + if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; + #endif /* SO_REUSE */ + } + } + } + prev = (struct tcp_pcb *)lpcb; + } +#if SO_REUSE + /* first try specific local IP */ + if (lpcb == NULL) { + /* only pass to ANY if no specific local IP has been found */ + lpcb = lpcb_any; + prev = lpcb_prev; + } +#endif /* SO_REUSE */ + if (lpcb == NULL && netif_pcb) { + lpcb = netif_pcb; + prev = netif_pcb_prev; + } + if (lpcb != NULL) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + if (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + goto aborted; + } + } + tcp_input_pcb = pcb; + err = tcp_process(pcb); + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + if (!(pcb->flags & TF_RXCLOSED)) { + /* Connection closed although the application has only shut down the + tx side: call the PCB's err callback and indicate the closure to + ensure the application doesn't continue using the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD); + } + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + + if (recv_data != NULL) { + LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL); + if (pcb->flags & TF_RXCLOSED) { + /* received data although already closed -> abort (send RST) to + notify the remote host that not all data has been processed */ + pbuf_free(recv_data); + tcp_abort(pcb); + goto aborted; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* correct rcv_wnd as the application won't call tcp_recved() + for the FIN's seqno */ + if (pcb->rcv_wnd != TCP_WND) { + pcb->rcv_wnd++; + } + TCP_EVENT_CLOSED(pcb, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + } + + tcp_input_pcb = NULL; + /* Try to send something out. */ + tcp_output(pcb); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + } + } + /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()). + Below this line, 'pcb' may not be dereferenced! */ +aborted: + tcp_input_pcb = NULL; + recv_data = NULL; + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + inseg.p = NULL; + } + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); +} + +/** + * Called by tcp_input() when a segment arrives for a listening + * connection (from tcp_input()). + * + * @param pcb the tcp_pcb_listen for which a segment arrived + * @return ERR_OK if the segment was processed + * another err_t on error + * + * @note the return value is not (yet?) used in tcp_input() + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); +#if TCP_LISTEN_BACKLOG + if (pcb->accepts_pending >= pcb->backlog) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); + return ERR_ABRT; + } +#endif /* TCP_LISTEN_BACKLOG */ + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ +#if LWIP_IPV6 + PCB_ISIPV6(npcb) = ip_current_is_v6(); +#endif /* LWIP_IPV6 */ + ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); + ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); + npcb->bound_to_netif = pcb->bound_to_netif; + npcb->local_port = tcphdr->dest; + memcpy(npcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + npcb->snd_wnd = tcphdr->wnd; + npcb->snd_wnd_max = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & SOF_INHERITED; + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG_ACTIVE(npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, + &npcb->remote_ip, PCB_ISIPV6(npcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + snmp_inc_tcppassiveopens(); + + /* Send a SYN|ACK together with the MSS option. */ + rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK); + if (rc != ERR_OK) { + tcp_abandon(npcb, 0); + return rc; + } + return tcp_output(npcb); + } + return ERR_OK; +} + +/** + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + return tcp_output(pcb); + } + return ERR_OK; +} + +/** + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + * + * @param pcb the tcp_pcb for which a segment arrived + * + * @note the segment which arrived is saved in global variables, therefore only the pcb + * involved is passed as a parameter to this function + */ +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags |= TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) { + /* Cope with new connection attempt after remote end crashed */ + tcp_ack_now(pcb); + return ERR_OK; + } + + if ((pcb->flags & TF_RXCLOSED) == 0) { + /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */ + pcb->tmr = tcp_ticks; + } + pcb->keep_cnt_sent = 0; + + tcp_parseopt(pcb); + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->rcv_ann_right_edge = pcb->rcv_nxt; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wnd_max = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + +#if TCP_CALCULATE_EFF_SEND_MSS + pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, + PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + + /* Set ssthresh again after changing pcb->mss (already set in tcp_connect + * but for the default value of pcb->mss) */ + pcb->ssthresh = pcb->mss * 10; + + pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else { + pcb->rtime = 0; + pcb->nrtx = 0; + } + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + if (err == ERR_ABRT) { + return ERR_ABRT; + } + tcp_ack_now(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + u16_t old_cwnd; + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + /* Already aborted? */ + if (err != ERR_ABRT) { + tcp_abort(pcb); + } + return ERR_ABRT; + } + old_cwnd = pcb->cwnd; + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + + /* Prevent ACK for SYN to generate a sent event */ + if (pcb->acked != 0) { + pcb->acked--; + } + + pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); + + if (recv_flags & TF_GOT_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + } else { + /* incorrect ACK number, send RST */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { + /* Looks like another copy of the SYN - retransmit our SYN-ACK */ + tcp_rexmit(pcb); + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { /* passive close */ + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (recv_flags & TF_GOT_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */ + recv_flags |= TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +#if TCP_QUEUE_OOSEQ +/** + * Insert segment into the list (segments covered with new one will be deleted) + * + * Called from tcp_receive() + */ +static void +tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next) +{ + struct tcp_seg *old_seg; + + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + /* received segment overlaps all following segments */ + tcp_segs_free(next); + next = NULL; + } + else { + /* delete some following segments + oos queue may have segments with FIN flag */ + while (next && + TCP_SEQ_GEQ((seqno + cseg->len), + (next->tcphdr->seqno + next->len))) { + /* cseg with FIN already processed */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + if (next && + TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + cseg->len = (u16_t)(next->tcphdr->seqno - seqno); + pbuf_realloc(cseg->p, cseg->len); + } + } + cseg->next = next; +} +#endif /* TCP_QUEUE_OOSEQ */ + +/** + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + * + * Called from tcp_process(). + */ +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif /* TCP_QUEUE_OOSEQ */ + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + int found_dupack = 0; +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < tcphdr->wnd) { + pcb->snd_wnd_max = tcphdr->wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + pcb->persist_backoff = 0; + } + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, + ("tcp_receive: no window update lastack %"U32_F" ackno %" + U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a + * duplicate ack if: + * 1) It doesn't ACK new data + * 2) length of received packet is zero (i.e. no payload) + * 3) the advertised window hasn't changed + * 4) There is outstanding unacknowledged data (retransmission timer running) + * 5) The ACK is == biggest ACK sequence number so far seen (snd_una) + * + * If it passes all five, should process as a dupack: + * a) dupacks < 3: do nothing + * b) dupacks == 3: fast retransmit + * c) dupacks > 3: increase cwnd + * + * If it only passes 1-3, should reset dupack counter (and add to + * stats, which we don't do in lwIP) + * + * If it only passes 1, should reset dupack counter + * + */ + + /* Clause 1 */ + if (TCP_SEQ_LEQ(ackno, pcb->lastack)) { + pcb->acked = 0; + /* Clause 2 */ + if (tcplen == 0) { + /* Clause 3 */ + if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){ + /* Clause 4 */ + if (pcb->rtime >= 0) { + /* Clause 5 */ + if (pcb->lastack == ackno) { + found_dupack = 1; + if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) { + ++pcb->dupacks; + } + if (pcb->dupacks > 3) { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* Do fast retransmit */ + tcp_rexmit_fast(pcb); + } + } + } + } + } + /* If Clause (1) or more is true, but not a duplicate ack, reset + * count of consecutive duplicate acks */ + if (!found_dupack) { + pcb->dupacks = 0; + } + } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. Diff between the two can never exceed 64K? */ + pcb->acked = (u16_t)(ackno - pcb->lastack); + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + + /* If there's nothing left to acknowledge, stop the retransmit + timer, otherwise reset it to start again */ + if(pcb->unacked == NULL) + pcb->rtime = -1; + else + pcb->rtime = 0; + + pcb->polltmr = 0; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { + /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p))); + /* Prevent ACK for FIN to generate a sent event */ + if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) { + pcb->acked--; + } + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + /* diff between this shouldn't exceed 32K since this are tcp timer ticks + and a round-trip shouldn't be that long... */ + m = (s16_t)(tcp_ticks - pcb->rttest); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further unless the pcb already received a FIN. + (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){ + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + off = pcb->rcv_nxt - seqno; + p = inseg.p; + LWIP_ASSERT("inseg.p != NULL", inseg.p); + LWIP_ASSERT("insane offset!", (off < 0x7fff)); + if (inseg.p->len < off) { + LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off)); + new_tot_len = (u16_t)(inseg.p->tot_len - off); + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + if(pbuf_header(p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } else { + if(pbuf_header(inseg.p, (s16_t)-off)) { + /* Do we need to cope with this failing? Assert for now */ + LWIP_ASSERT("pbuf_header failed", 0); + } + } + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, + pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ + tcplen = TCP_TCPLEN(&inseg); + + if (tcplen > pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + inseg.len = pcb->rcv_wnd; + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } +#if TCP_QUEUE_OOSEQ + /* Received in-sequence data, adjust ooseq data if: + - FIN has been received or + - inseq overlaps with ooseq */ + if (pcb->ooseq != NULL) { + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: received in-order FIN, binning ooseq queue\n")); + /* Received in-order FIN means anything that was received + * out of order must now have been received in-order, so + * bin the ooseq queue */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } else { + next = pcb->ooseq; + /* Remove all segments on ooseq that are covered by inseg already. + * FIN is copied from ooseq to inseg if present. */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg cannot have FIN here (already processed above) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + prev = next; + next = next->next; + tcp_seg_free(prev); + } + /* Now trim right side of inseg if it overlaps with the first + * segment on ooseq */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* inseg cannot have FIN here (already processed above) */ + inseg.len = (u16_t)(next->tcphdr->seqno - seqno); + if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) { + inseg.len -= 1; + } + pbuf_realloc(inseg.p, inseg.len); + tcplen = TCP_TCPLEN(&inseg); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n", + (seqno + tcplen) == next->tcphdr->seqno); + } + pcb->ooseq = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + pcb->rcv_nxt = seqno + tcplen; + + /* Update the receiver's (our) window. */ + LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen); + pcb->rcv_wnd -= tcplen; + + tcp_update_rcv_ann_wnd(pcb); + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags |= TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + are now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n", + pcb->rcv_wnd >= TCP_TCPLEN(cseg)); + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + + tcp_update_rcv_ann_wnd(pcb); + + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags |= TF_GOT_FIN; + if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */ + pcb->state = CLOSE_WAIT; + } + } + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_send_empty_ack(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace some segments with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + pcb->ooseq = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } else { + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) { + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim trim the previous + segment, delete next segments that included in received segment + and trim received, if needed. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = (u16_t)(seqno - prev->tcphdr->seqno); + pbuf_realloc(prev->p, prev->len); + } + prev->next = cseg; + tcp_oos_insert_segment(cseg, next); + } + break; + } + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) { + /* segment "next" already contains all data */ + break; + } + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = (u16_t)(seqno - next->tcphdr->seqno); + pbuf_realloc(next->p, next->len); + } + /* check if the remote side overruns our receive window */ + if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, + ("tcp_receive: other end overran receive window" + "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n", + seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd)); + if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) { + /* Must remove the FIN from the header as we're trimming + * that byte of sequence-space from the packet */ + TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN); + } + /* Adjust length of segment to fit in the window. */ + next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno; + pbuf_realloc(next->next->p, next->next->len); + tcplen = TCP_TCPLEN(next->next); + LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n", + (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd)); + } + } + break; + } + } + prev = next; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#endif /* TCP_QUEUE_OOSEQ */ + } + } else { + /* The incoming segment is not withing the window. */ + tcp_send_empty_ack(pcb); + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } +} + +/** + * Parses the options contained in the incoming segment. + * + * Called from tcp_listen_input() and tcp_process(). + * Currently, only the MSS option is supported! + * + * @param pcb the tcp_pcb for which a segment arrived + */ +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u16_t c, max_c; + u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (c = 0; c < max_c; ) { + opt = opts[c]; + switch (opt) { + case 0x00: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: + /* NOP option. */ + ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case 0x02: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (opts[c + 1] != 0x04 || c + 0x04 > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + /* Advance to next option */ + c += 0x04; + break; +#if LWIP_TCP_TIMESTAMPS + case 0x08: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (opts[c + 1] != 0x0A || c + 0x0A > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = (opts[c+2]) | (opts[c+3] << 8) | + (opts[c+4] << 16) | (opts[c+5] << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option */ + c += 0x0A; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + if (opts[c + 1] == 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + /* If the length field is zero, the options are malformed + and we don't process them further. */ + return; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/external/badvpn_dns/lwip/src/core/tcp_out.c b/external/badvpn_dns/lwip/src/core/tcp_out.c new file mode 100644 index 00000000..b9fc339e --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/tcp_out.c @@ -0,0 +1,1499 @@ +/** + * @file + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp_impl.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#include + +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +/** Allocate a pbuf and create a tcphdr at p->payload, used for output + * functions other than the default tcp_output -> tcp_output_segment + * (e.g. tcp_send_empty_ack, etc.) + * + * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) + * @param optlen length of header-options + * @param datalen length of tcp data to reserve in pbuf + * @param seqno_be seqno in network byte order (big-endian) + * @return pbuf with p->payload being the tcp_hdr + */ +static struct pbuf * +tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr; + struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); + if (p != NULL) { + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= TCP_HLEN + optlen)); + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = seqno_be; + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + /* If we're sending a packet, update the announced right window edge */ + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + } + return p; +} + +/** + * Called by tcp_close() to send a segment including FIN flag but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_fin(struct tcp_pcb *pcb) +{ + /* first, try to add the fin to the last unsent segment */ + if (pcb->unsent != NULL) { + struct tcp_seg *last_unsent; + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { + /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ + TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); + pcb->flags |= TF_FIN; + return ERR_OK; + } + } + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue_flags(pcb, TCP_FIN); +} + +/** + * Create a TCP segment with prefilled header. + * + * Called by tcp_write and tcp_enqueue_flags. + * + * @param pcb Protocol control block for the TCP connection. + * @param p pbuf that is used to hold the TCP header. + * @param flags TCP flags for header. + * @param seqno TCP sequence number of this packet + * @param optflags options to include in TCP header + * @return a new tcp_seg pointing to p, or NULL. + * The TCP header is filled in except ackno and wnd. + * p is freed on failure. + */ +static struct tcp_seg * +tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) +{ + struct tcp_seg *seg; + u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); + + if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); + pbuf_free(p); + return NULL; + } + seg->flags = optflags; + seg->next = NULL; + seg->p = p; + seg->len = p->tot_len - optlen; +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* build TCP header */ + if (pbuf_header(p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + tcp_seg_free(seg); + return NULL; + } + seg->tcphdr = (struct tcp_hdr *)seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + /* ackno is set in tcp_output */ + TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); + /* wnd and chksum are set in tcp_output */ + seg->tcphdr->urgp = 0; + return seg; +} + +/** + * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. + * + * This function is like pbuf_alloc(layer, length, PBUF_RAM) except + * there may be extra bytes available at the end. + * + * @param layer flag to define header size. + * @param length size of the pbuf's payload. + * @param max_length maximum usable size of payload+oversize. + * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. + * @param pcb The TCP connection that willo enqueue the pbuf. + * @param apiflags API flags given to tcp_write. + * @param first_seg true when this pbuf will be used in the first enqueued segment. + * @param + */ +#if TCP_OVERSIZE +static struct pbuf * +tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, + u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, + u8_t first_seg) +{ + struct pbuf *p; + u16_t alloc = length; + +#if LWIP_NETIF_TX_SINGLE_PBUF + LWIP_UNUSED_ARG(max_length); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(apiflags); + LWIP_UNUSED_ARG(first_seg); + /* always create MSS-sized pbufs */ + alloc = max_length; +#else /* LWIP_NETIF_TX_SINGLE_PBUF */ + if (length < max_length) { + /* Should we allocate an oversized pbuf, or just the minimum + * length required? If tcp_write is going to be called again + * before this segment is transmitted, we want the oversized + * buffer. If the segment will be transmitted immediately, we can + * save memory by allocating only length. We use a simple + * heuristic based on the following information: + * + * Did the user set TCP_WRITE_FLAG_MORE? + * + * Will the Nagle algorithm defer transmission of this segment? + */ + if ((apiflags & TCP_WRITE_FLAG_MORE) || + (!(pcb->flags & TF_NODELAY) && + (!first_seg || + pcb->unsent != NULL || + pcb->unacked != NULL))) { + alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); + } + } +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + p = pbuf_alloc(layer, alloc, PBUF_RAM); + if (p == NULL) { + return NULL; + } + LWIP_ASSERT("need unchained pbuf", p->next == NULL); + *oversize = p->len - length; + /* trim p->len to the currently used size */ + p->len = p->tot_len = length; + return p; +} +#else /* TCP_OVERSIZE */ +#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) +#endif /* TCP_OVERSIZE */ + +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + +/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). + * + * @param pcb the tcp pcb to check for + * @param len length of data to send (checked agains snd_buf) + * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise + */ +static err_t +tcp_write_checks(struct tcp_pcb *pcb, u16_t len) +{ + /* connection is in invalid state for data transmission? */ + if ((pcb->state != ESTABLISHED) && + (pcb->state != CLOSE_WAIT) && + (pcb->state != SYN_SENT) && + (pcb->state != SYN_RCVD)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } else if (len == 0) { + return ERR_OK; + } + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", + len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + return ERR_OK; +} + +/** + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @param pcb Protocol control block for the TCP connection to enqueue data for. + * @param arg Pointer to the data to be enqueued for sending. + * @param len Data length in bytes + * @param apiflags combination of following flags : + * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack + * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, + * @return ERR_OK if enqueued, another err_t on error + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) +{ + struct pbuf *concat_p = NULL; + struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; + u16_t pos = 0; /* position in 'arg' data */ + u16_t queuelen; + u8_t optlen = 0; + u8_t optflags = 0; +#if TCP_OVERSIZE + u16_t oversize = 0; + u16_t oversize_used = 0; +#endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + err_t err; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); + +#if LWIP_NETIF_TX_SINGLE_PBUF + /* Always copy to try to create single pbufs for TX */ + apiflags |= TCP_WRITE_FLAG_COPY; +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)apiflags)); + LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", + arg != NULL, return ERR_ARG;); + + err = tcp_write_checks(pcb, len); + if (err != ERR_OK) { + return err; + } + queuelen = pcb->snd_queuelen; + +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif /* LWIP_TCP_TIMESTAMPS */ + + + /* + * TCP segmentation is done in three phases with increasing complexity: + * + * 1. Copy data directly into an oversized pbuf. + * 2. Chain a new pbuf to the end of pcb->unsent. + * 3. Create new segments. + * + * We may run out of memory at any point. In that case we must + * return ERR_MEM and not change anything in pcb. Therefore, all + * changes are recorded in local variables and committed at the end + * of the function. Some pcb fields are maintained in local copies: + * + * queuelen = pcb->snd_queuelen + * oversize = pcb->unsent_oversize + * + * These variables are set consistently by the phases: + * + * seg points to the last segment tampered with. + * + * pos records progress as data is segmented. + */ + + /* Find the tail of the unsent queue. */ + if (pcb->unsent != NULL) { + u16_t space; + u16_t unsent_optlen; + + /* @todo: this could be sped up by keeping last_unsent in the pcb */ + for (last_unsent = pcb->unsent; last_unsent->next != NULL; + last_unsent = last_unsent->next); + + /* Usable space at the end of the last unsent segment */ + unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags); + space = mss_local - (last_unsent->len + unsent_optlen); + + /* + * Phase 1: Copy data directly into an oversized pbuf. + * + * The number of bytes copied is recorded in the oversize_used + * variable. The actual copying is done at the bottom of the + * function. + */ +#if TCP_OVERSIZE +#if TCP_OVERSIZE_DBGCHECK + /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ + LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", + pcb->unsent_oversize == last_unsent->oversize_left); +#endif /* TCP_OVERSIZE_DBGCHECK */ + oversize = pcb->unsent_oversize; + if (oversize > 0) { + LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); + seg = last_unsent; + oversize_used = oversize < len ? oversize : len; + pos += oversize_used; + oversize -= oversize_used; + space -= oversize_used; + } + /* now we are either finished or oversize is zero */ + LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: Chain a new pbuf to the end of pcb->unsent. + * + * We don't extend segments containing SYN/FIN flags or options + * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at + * the end. + */ + if ((pos < len) && (space > 0) && (last_unsent->len > 0)) { + u16_t seglen = space < len - pos ? space : len - pos; + seg = last_unsent; + + /* Create a pbuf with a copy or reference to seglen bytes. We + * can use PBUF_RAW here since the data appears in the middle of + * a segment. A header will never be prepended. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* Data is copied */ + if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", + seglen)); + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + last_unsent->oversize_left += oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + } else { + /* Data is not copied */ + if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, + ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + concat_p->payload = (u8_t*)arg + pos; + } + + pos += seglen; + queuelen += pbuf_clen(concat_p); + } + } else { +#if TCP_OVERSIZE + LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", + pcb->unsent_oversize == 0); +#endif /* TCP_OVERSIZE */ + } + + /* + * Phase 3: Create new segments. + * + * The new segments are chained together in the local 'queue' + * variable, ready to be appended to pcb->unsent. + */ + while (pos < len) { + struct pbuf *p; + u16_t left = len - pos; + u16_t max_len = mss_local - optlen; + u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ + + if (apiflags & TCP_WRITE_FLAG_COPY) { + /* If copy is set, memory should be allocated and data copied + * into pbuf */ + if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", + (p->len >= seglen)); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); + } else { + /* Copy is not set: First allocate a pbuf for holding the data. + * Since the referenced data is available at least until it is + * sent out on the link (as it has to be ACKed by the remote + * party) we can safely use PBUF_ROM instead of PBUF_REF here. + */ + struct pbuf *p2; +#if TCP_OVERSIZE + LWIP_ASSERT("oversize == 0", oversize == 0); +#endif /* TCP_OVERSIZE */ + if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ + /* reference the non-volatile payload data */ + p2->payload = (u8_t*)arg + pos; + + /* Second, allocate a pbuf for the headers. */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p2); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n")); + goto memerr; + } + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(p/*header*/, p2/*data*/); + } + + queuelen += pbuf_clen(p); + + /* Now that there are more segments queued, we check again if the + * length of the queue exceeds the configured maximum or + * overflows. */ + if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + pbuf_free(p); + goto memerr; + } + + if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { + goto memerr; + } +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = oversize; +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); + prev_seg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + prev_seg = seg; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); + + pos += seglen; + } + + /* + * All three segmentation phases were successful. We can commit the + * transaction. + */ + + /* + * Phase 1: If data has been added to the preallocated tail of + * last_unsent, we update the length fields of the pbuf chain. + */ +#if TCP_OVERSIZE + if (oversize_used > 0) { + struct pbuf *p; + /* Bump tot_len of whole chain, len of tail */ + for (p = last_unsent->p; p; p = p->next) { + p->tot_len += oversize_used; + if (p->next == NULL) { + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); + p->len += oversize_used; + } + } + last_unsent->len += oversize_used; +#if TCP_OVERSIZE_DBGCHECK + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + last_unsent->oversize_left -= oversize_used; +#endif /* TCP_OVERSIZE_DBGCHECK */ + } + pcb->unsent_oversize = oversize; +#endif /* TCP_OVERSIZE */ + + /* + * Phase 2: concat_p can be concatenated onto last_unsent->p + */ + if (concat_p != NULL) { + LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", + (last_unsent != NULL)); + pbuf_cat(last_unsent->p, concat_p); + last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ + } + + /* + * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that + * is harmless + */ + if (last_unsent == NULL) { + pcb->unsent = queue; + } else { + last_unsent->next = queue; + } + + /* + * Finally update the pcb state. + */ + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queuelen; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", + pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued. */ + if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + + if (concat_p != NULL) { + pbuf_free(concat_p); + } + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/** + * Enqueue TCP options for transmission. + * + * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl(). + * + * @param pcb Protocol control block for the TCP connection. + * @param flags TCP header flags to set in the outgoing segment. + * @param optdata pointer to TCP options, or NULL. + * @param optlen length of TCP options in bytes. + */ +err_t +tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags) +{ + struct pbuf *p; + struct tcp_seg *seg; + u8_t optflags = 0; + u8_t optlen = 0; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)", + (flags & (TCP_SYN | TCP_FIN)) != 0); + + /* check for configured max queuelen and possible overflow */ + if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n", + pcb->snd_queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + + if (flags & TCP_SYN) { + optflags = TF_SEG_OPTS_MSS; + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + optflags |= TF_SEG_OPTS_TS; + } +#endif /* LWIP_TCP_TIMESTAMPS */ + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* tcp_enqueue_flags is always called with either SYN or FIN in flags. + * We need one available snd_buf byte to do that. + * This means we can't send FIN while snd_buf==0. A better fix would be to + * not include SYN and FIN sequence numbers in the snd_buf count. */ + if (pcb->snd_buf == 0) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + + /* Allocate pbuf with room for TCP header + options */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen", + (p->len >= optlen)); + + /* Allocate memory for tcp_seg, and fill in fields. */ + if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) { + pcb->flags |= TF_NAGLEMEMERR; + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0); + LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0); + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, + ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + /* Now append seg to pcb->unsent queue */ + if (pcb->unsent == NULL) { + pcb->unsent = seg; + } else { + struct tcp_seg *useg; + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + useg->next = seg; + } +#if TCP_OVERSIZE + /* The new unsent tail has no space */ + pcb->unsent_oversize = 0; +#endif /* TCP_OVERSIZE */ + + /* SYN and FIN bump the sequence number */ + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + pcb->snd_lbb++; + /* optlen does not influence snd_buf */ + pcb->snd_buf--; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + + /* update number of segments on the queues */ + pcb->snd_queuelen += pbuf_clen(seg->p); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue_flags: invalid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + return ERR_OK; +} + +#if LWIP_TCP_TIMESTAMPS +/* Build a timestamp option (12 bytes long) at the specified options pointer) + * + * @param pcb tcp_pcb + * @param opts option pointer where to store the timestamp option + */ +static void +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = PP_HTONL(0x0101080A); + opts[1] = htonl(sys_now()); + opts[2] = htonl(pcb->ts_recent); +} +#endif + +/** Send an ACK without data. + * + * @param pcb Protocol control block for the TCP connection to send the ACK + */ +err_t +tcp_send_empty_ack(struct tcp_pcb *pcb) +{ + struct pbuf *p; + u8_t optlen = 0; +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + +#if LWIP_TCP_TIMESTAMPS + if (pcb->flags & TF_TIMESTAMP) { + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + } +#endif + + p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + /* NB. MSS option is only sent on SYNs, so ignore it here */ +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (pcb->flags & TF_TIMESTAMP) { + tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1)); + } +#endif + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + pbuf_free(p); + + return ERR_OK; +} + +/** + * Find out what we can send and send it + * + * @param pcb Protocol control block for the TCP connection to send data + * @return ERR_OK if data has been sent or nothing to send + * another err_t on error + */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg, *useg; + u32_t wnd, snd_nxt; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + return tcp_send_empty_ack(pcb); + } + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", + (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F + ", cwnd %"U16_F", wnd %"U32_F + ", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, + ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F + ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? */ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { + LWIP_ASSERT("RST not expected here!", + (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0); + /* Stop sending if the nagle algorithm would prevent it + * Don't stop: + * - if tcp_write had a memory error before (prevent delayed ACK timeout) or + * - if FIN was already enqueued for this PCB (SYN is always alone in a segment - + * either seg->next != NULL or pcb->unacked == NULL; + * RST is no sent using tcp_write/tcp_output. + */ + if((tcp_do_output_nagle(pcb) == 0) && + ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){ + break; + } +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather somewhere before it. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) { + /* add segment to before tail of unacked list, keeping the list sorted */ + struct tcp_seg **cur_seg = &(pcb->unacked); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = (*cur_seg); + (*cur_seg) = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + /* last unsent has been removed, reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + pcb->flags &= ~TF_NAGLEMEMERR; + return ERR_OK; +} + +/** + * Called by tcp_output() to actually send a TCP segment over IP. + * + * @param seg the tcp_seg to send + * @param pcb the tcp_pcb for the TCP connection used to send the segment + */ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + u32_t *opts; + + /** @bug Exclude retransmitted segments from this count. */ + snmp_inc_tcpoutsegs(); + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + + pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; + + /* Add any requested options. NB MSS option is only set on SYN + packets, so ignore it here */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + opts += 1; + } +#if LWIP_TCP_TIMESTAMPS + pcb->ts_lastacksent = pcb->rcv_nxt; + + if (seg->flags & TF_SEG_OPTS_TS) { + tcp_build_timestamp_option(pcb, opts); + opts += 3; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + return; + } + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ +#endif /* TCP_CHECKSUM_ON_COPY */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + pcb->tos, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ +} + +/** + * Send a TCP RESET packet (empty segment with RST flag set) either to + * abort a connection or to show that there is no matching local connection + * for a received segment. + * + * Called by tcp_abort() (to abort a local connection), tcp_input() (if no + * matching local pcb was found), tcp_listen_input() (if incoming segment + * has ACK flag set) and tcp_process() (received segment in the wrong state) + * + * Since a RST segment is in most cases not sent for an active connection, + * tcp_rst() has a number of arguments that are taken from a tcp_pcb for + * most other segment output functions. + * + * @param seqno the sequence number to use for the outgoing segment + * @param ackno the acknowledge number to use for the outgoing segment + * @param local_ip the local IP address to send the segment from + * @param remote_ip the remote IP address to send the segment to + * @param local_port the local TCP port to send the segment from + * @param remote_port the remote TCP port to send the segment to + */ +void +tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = (struct tcp_hdr *)p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK); + tcphdr->wnd = PP_HTONS(TCP_WND); + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/** + * Requeue all unacked segments for retransmission + * + * Called by tcp_slowtmr() for slow retransmission. + * + * @param pcb the tcp_pcb for which to re-enqueue all unacked segments + */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + /* last unsent hasn't changed, no need to reset unsent_oversize */ + + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +/** + * Requeue the first unacked segment for retransmission + * + * Called by tcp_receive() for fast retramsmit. + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + struct tcp_seg **cur_seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + /* Keep the unsent queue sorted. */ + seg = pcb->unacked; + pcb->unacked = seg->next; + + cur_seg = &(pcb->unsent); + while (*cur_seg && + TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) { + cur_seg = &((*cur_seg)->next ); + } + seg->next = *cur_seg; + *cur_seg = seg; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + snmp_inc_tcpretranssegs(); + /* No need to call tcp_output: we are always called from tcp_input() + and thus tcp_output directly returns. */ +} + + +/** + * Handle retransmission after three dupacks received + * + * @param pcb the tcp_pcb for which to retransmit the first unacked segment + */ +void +tcp_rexmit_fast(struct tcp_pcb *pcb) +{ + if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: dupacks %"U16_F" (%"U32_F + "), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + + /* Set ssthresh to half of the minimum of the current + * cwnd and the advertised window */ + if (pcb->cwnd > pcb->snd_wnd) { + pcb->ssthresh = pcb->snd_wnd / 2; + } else { + pcb->ssthresh = pcb->cwnd / 2; + } + + /* The minimum value for ssthresh should be 2 MSS */ + if (pcb->ssthresh < 2*pcb->mss) { + LWIP_DEBUGF(TCP_FR_DEBUG, + ("tcp_receive: The minimum value for ssthresh %"U16_F + " should be min 2 mss %"U16_F"...\n", + pcb->ssthresh, 2*pcb->mss)); + pcb->ssthresh = 2*pcb->mss; + } + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } +} + + +/** + * Send keepalive packets to keep a connection active although + * no data is sent over it. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a keepalive packet + */ +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; +#if CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* CHECKSUM_GEN_TCP */ + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } +#if CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; + + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + + +/** + * Send persist timer zero-window probes to keep a connection active + * when a window update is lost. + * + * Called by tcp_slowtmr() + * + * @param pcb the tcp_pcb for which to send a zero-window probe packet + */ +void +tcp_zero_window_probe(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg; + u16_t len; + u8_t is_fin; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_zero_window_probe: tcp_ticks %"U32_F + " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n", + tcp_ticks, pcb->tmr, pcb->keep_cnt_sent)); + + seg = pcb->unacked; + + if(seg == NULL) { + seg = pcb->unsent; + } + if(seg == NULL) { + return; + } + + is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); + /* we want to send one seqno: either FIN or data (no options) */ + len = is_fin ? 0 : 1; + + p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + tcphdr = (struct tcp_hdr *)p->payload; + + if (is_fin) { + /* FIN segment, no data */ + TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN); + } else { + /* Data segment, copy in one byte from the head of the unacked queue */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); +#endif /* LWIP_NETIF_HWADDRHINT*/ + + pbuf_free(p); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F + " ackno %"U32_F".\n", + pcb->snd_nxt - 1, pcb->rcv_nxt)); +} +#endif /* LWIP_TCP */ diff --git a/external/badvpn_dns/lwip/src/core/timers.c b/external/badvpn_dns/lwip/src/core/timers.c new file mode 100644 index 00000000..c8ead4e7 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/timers.c @@ -0,0 +1,546 @@ +/** + * @file + * Stack-internal timers implementation. + * This file includes timer callbacks for stack-internal timers as well as + * functions to set up or stop timers and check for expired timers. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include "lwip/opt.h" + +#include "lwip/timers.h" +#include "lwip/tcp_impl.h" + +#if LWIP_TIMERS + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +#include "lwip/ip_frag.h" +#include "netif/etharp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout; +#if NO_SYS +static u32_t timeouts_last_time; +#endif /* NO_SYS */ + +#if LWIP_TCP +/** global variable that shows if the tcp timer is currently scheduled or not */ +static int tcpip_tcp_timer_active; + +/** + * Timer callback function that calls tcp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +tcpip_tcp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + /* call TCP timer handler */ + tcp_tmr(); + /* timer still needed? */ + if (tcp_active_pcbs || tcp_tw_pcbs) { + /* restart timer */ + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } else { + /* disable timer */ + tcpip_tcp_timer_active = 0; + } +} + +/** + * Called from TCP_REG when registering a new PCB: + * the reason is to have the TCP timer only running when + * there are active (or time-wait) PCBs. + */ +void +tcp_timer_needed(void) +{ + /* timer is off but needed again? */ + if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + /* enable and start timer */ + tcpip_tcp_timer_active = 1; + sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); + } +} +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n")); + ip_reass_tmr(); + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +} +#endif /* IP_REASSEMBLY */ + +#if LWIP_ARP +/** + * Timer callback function that calls etharp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n")); + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif /* LWIP_ARP */ + +#if LWIP_DHCP +/** + * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_coarse_tmr(); + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); +} + +/** + * Timer callback function that calls dhcp_fine_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dhcp_timer_fine(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n")); + dhcp_fine_tmr(); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +} +#endif /* LWIP_DHCP */ + +#if LWIP_AUTOIP +/** + * Timer callback function that calls autoip_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +autoip_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n")); + autoip_tmr(); + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +} +#endif /* LWIP_AUTOIP */ + +#if LWIP_IGMP +/** + * Timer callback function that calls igmp_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +igmp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n")); + igmp_tmr(); + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +} +#endif /* LWIP_IGMP */ + +#if LWIP_DNS +/** + * Timer callback function that calls dns_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +dns_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +/** Initialize this module */ +void sys_timeouts_init(void) +{ +#if IP_REASSEMBLY + sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL); +#endif /* IP_REASSEMBLY */ +#if LWIP_ARP + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif /* LWIP_ARP */ +#if LWIP_DHCP + sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL); + sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL); +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL); +#endif /* LWIP_AUTOIP */ +#if LWIP_IGMP + sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL); +#endif /* LWIP_IGMP */ +#if LWIP_DNS + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_now(); +#endif +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_timeouts_mbox_fetch() + * - by calling sys_check_timeouts() (NO_SYS==1 only) + * + * @param msecs time in milliseconds after that the timer should expire + * @param handler callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +#if LWIP_DEBUG_TIMERNAMES +void +sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name) +#else /* LWIP_DEBUG_TIMERNAMES */ +void +sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) +#endif /* LWIP_DEBUG_TIMERNAMES */ +{ + struct sys_timeo *timeout, *t; + + timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; + timeout->time = msecs; +#if LWIP_DEBUG_TIMERNAMES + timeout->handler_name = handler_name; + LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n", + (void *)timeout, msecs, handler_name, (void *)arg)); +#endif /* LWIP_DEBUG_TIMERNAMES */ + + if (next_timeout == NULL) { + next_timeout = timeout; + return; + } + + if (next_timeout->time > msecs) { + next_timeout->time -= msecs; + timeout->next = next_timeout; + next_timeout = timeout; + } else { + for(t = next_timeout; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } +} + +/** + * Go through timeout list (for this task only) and remove the first matching + * entry, even though the timeout has not triggered yet. + * + * @note This function only works as expected if there is only one timeout + * calling 'handler' in the list of timeouts. + * + * @param handler callback function that would be called by the timeout + * @param arg callback argument that would be passed to handler +*/ +void +sys_untimeout(sys_timeout_handler handler, void *arg) +{ + struct sys_timeo *prev_t, *t; + + if (next_timeout == NULL) { + return; + } + + for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == handler) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + next_timeout = t->next; + } else { + prev_t->next = t->next; + } + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) { + t->next->time += t->time; + } + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + +#if NO_SYS + +/** Handle timeouts for NO_SYS==1 (i.e. without using + * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout + * handler functions when timeouts expire. + * + * Must be called periodically from your main loop. + */ +void +sys_check_timeouts(void) +{ + if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time = now; + diff -= tmptimeout->time; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + handler(arg); + } + } + /* repeat until all expired timers have been called */ + }while(had_one); + } +} + +/** Set back the timestamp of the last call to sys_check_timeouts() + * This is necessary if sys_check_timeouts() hasn't been called for a long + * time (e.g. while saving energy) to prevent all timer functions of that + * period being called. + */ +void +sys_restart_timeouts(void) +{ + timeouts_last_time = sys_now(); +} + +#else /* NO_SYS */ + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeo *tmptimeout; + sys_timeout_handler handler; + void *arg; + + again: + if (!next_timeout) { + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (next_timeout->time > 0) { + time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time); + } else { + time_needed = SYS_ARCH_TIMEOUT; + } + + if (time_needed == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = next_timeout; + next_timeout = tmptimeout->next; + handler = tmptimeout->h; + arg = tmptimeout->arg; +#if LWIP_DEBUG_TIMERNAMES + if (handler != NULL) { + LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n", + tmptimeout->handler_name, arg)); + } +#endif /* LWIP_DEBUG_TIMERNAMES */ + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (handler != NULL) { + /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the + timeout handler function. */ + LOCK_TCPIP_CORE(); + handler(arg); + UNLOCK_TCPIP_CORE(); + } + LWIP_TCPIP_THREAD_ALIVE(); + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time_needed < next_timeout->time) { + next_timeout->time -= time_needed; + } else { + next_timeout->time = 0; + } + } + } +} + +#endif /* NO_SYS */ + +#else /* LWIP_TIMERS */ +/* Satisfy the TCP code which calls this function */ +void +tcp_timer_needed(void) +{ +} +#endif /* LWIP_TIMERS */ diff --git a/external/badvpn_dns/lwip/src/core/udp.c b/external/badvpn_dns/lwip/src/core/udp.c new file mode 100644 index 00000000..ac0e56d1 --- /dev/null +++ b/external/badvpn_dns/lwip/src/core/udp.c @@ -0,0 +1,1151 @@ +/** + * @file + * User Datagram Protocol module + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). + * + */ + +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/icmp6.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +#if 0 + struct udp_pcb *ipcb = udp_pcbs; + while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == udp_port) { + /* port is already used by another udp_pcb */ + udp_port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + return 0; + } + return udp_port; +#endif +} + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and hands over the pbuf to the pcbs + * recv function. If no pcb is found or the datagram is incorrect, the + * pbuf is freed. + * + * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + u8_t for_us; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)p->payload; + + /* is broadcast packet ? */ +#if LWIP_IPV6 + broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#else /* LWIP_IPV6 */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#endif /* LWIP_IPV6 */ + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + /* convert src and dest ports to host byte order */ + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, ("udp (")); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); + +#if LWIP_DHCP + pcb = NULL; + /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by + the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ + if (dest == DHCP_CLIENT_PORT) { + /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ + if (src == DHCP_SERVER_PORT) { + if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { + /* accept the packe if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src + (no need to check for IPv6 since the dhcp struct always uses IPv4) */ + if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { + pcb = inp->dhcp->pcb; + } + } + } + } else +#endif /* LWIP_DHCP */ + { + prev = NULL; + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb. + * 'Perfect match' pcbs (connected to the remote port & ip address) are + * preferred. If no perfect match is found, the first unconnected pcb that + * matches the local port and ip address gets the datagram. */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + local_match = 0; + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if (pcb->local_port == dest) { + if ( +#if LWIP_IPV6 + ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && + (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(pcb) && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(pcb, SOF_BROADCAST) && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is + found faster next time */ + prev->next = pcb->next; + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } else { + UDP_STATS_INC(udp.cachehit); + } + break; + } + prev = pcb; + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr()); + } else +#endif /* LWIP_IPV6 */ + { + for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); + } + } + if (for_us) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP +#if LWIP_UDPLITE + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ + u16_t chklen = ntohs(udphdr->len); + if (chklen < sizeof(struct udp_hdr)) { + if (chklen == 0) { + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet (See RFC 3828 chap. 3.1) */ + chklen = p->tot_len; + } else { + /* At least the UDP-Lite header must be covered by the + checksum! (Again, see RFC 3828 chap. 3.1) */ + goto chkerr; + } + } + if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } else +#endif /* LWIP_UDPLITE */ + { + if (udphdr->chksum != 0) { + if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, + ipX_current_src_addr(), + ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } + } +#endif /* CHECKSUM_CHECK_UDP */ + if(pbuf_header(p, -UDP_HLEN)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + if (pcb != NULL) { + snmp_inc_udpindatagrams(); +#if SO_REUSE && SO_REUSE_RXTOALL + if ((broadcast || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && + ip_get_option(pcb, SOF_REUSEADDR)) { + /* pass broadcast- or multicast packets to all multicast pcbs + if SOF_REUSEADDR is set on the first match */ + struct udp_pcb *mpcb; + u8_t p_header_changed = 0; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); + for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { + if (mpcb != pcb) { + /* compare PCB local addr+port to UDP destination addr+port */ + if ((mpcb->local_port == dest) && +#if LWIP_IPV6 + ((PCB_ISIPV6(mpcb) && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(mpcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast))))) { +#endif /* IP_SOF_BROADCAST_RECV */ + /* pass a copy of the packet to all local matches */ + if (mpcb->recv.ip4 != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header(p, hdrs_len); + p_header_changed = 1; + } + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (q != NULL) { + err_t err = pbuf_copy(q, p); + if (err == ERR_OK) { + /* move payload to UDP data */ + pbuf_header(q, -hdrs_len); +#if LWIP_IPV6 + if (PCB_ISIPV6(mpcb)) { + mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -hdrs_len); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv.ip4 != NULL) { + /* now the recv function is responsible for freeing p */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); + } + } else { + /* no recv function registered? then we have to free the pbuf! */ + pbuf_free(p); + goto end; + } + } else { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); + +#if LWIP_ICMP || LWIP_ICMP6 + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { + /* move payload pointer back to ip header */ + pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); + icmp_port_unreach(ip_current_is_v6(), p); + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } +end: + PERF_STOP("udp_input"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * + * The datagram will be sent to the current remote_ip & remote_port + * stored in pcb. If the pcb is not bound to a port, it will + * automatically be bound to a random port. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * - More errors could be returned by lower protocol layers. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +/** Same as udp_send() but with checksum + */ +err_t +udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum) +{ + /* send to the packet using remote ip and port stored in the pcb */ + return udp_sendto_chksum(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +err_t +udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, u8_t have_chksum, u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct netif *netif; + ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + +#if LWIP_IPV6 || LWIP_IGMP + if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { + /* For multicast, find a netif based on source address. */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { +#if LWIP_IGMP + dst_ip_route = ip_2_ipX(&pcb->multicast_ip); +#endif /* LWIP_IGMP */ + } + } +#endif /* LWIP_IPV6 || LWIP_IGMP */ + + /* find the outgoing network interface for this packet */ + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** + * Send data to a specified address using UDP. + * The netif used for sending can be specified. + * + * This function exists mainly for DHCP, to be able to send UDP packets + * on a netif that is still down. + * + * @param pcb UDP PCB used to send the data. + * @param p chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * @param netif the netif used for sending. + * + * dst_ip & dst_port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code (@see udp_send for possible error codes) + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +err_t +udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, + u16_t dst_port, struct netif *netif, u8_t have_chksum, + u16_t chksum) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct udp_hdr *udphdr; + ip_addr_t *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + !PCB_ISIPV6(pcb) && +#endif /* LWIP_IPV6 */ + ip_addr_isbroadcast(dst_ip, netif) ) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); + return ERR_VAL; + } +#endif /* IP_SOF_BROADCAST */ + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); + return err; + } + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a separate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + if (p->tot_len != 0) { + /* chain header q in front of given pbuf p (only if p contains data) */ + pbuf_chain(q, p); + } + /* first pbuf q points to header pbuf */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* adding space for header within p succeeded */ + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", + (q->len >= sizeof(struct udp_hdr))); + /* q now represents the packet to be sent */ + udphdr = (struct udp_hdr *)q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* Multicast Loop? */ +#if LWIP_IGMP + if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && +#if LWIP_IPV6 + ( +#if LWIP_IPV6_MLD + (PCB_ISIPV6(pcb) && + ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || +#endif /* LWIP_IPV6_MLD */ + (!PCB_ISIPV6(pcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(dst_ip)))) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + + /* PCB local address is IP_ANY_ADDR? */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { + src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); + if (src_ip == NULL) { + /* No suitable source address was found. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + /* p is still referenced by the caller, and will live on */ + } + return ERR_RTE; + } + src_ip = ipX_2_ip(&pcb->local_ip); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip))) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* check if UDP PCB local IP address is correct + * this could be an old address if netif->ip_addr has changed */ + if (!ip_addr_cmp(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = ipX_2_ip(&(pcb->local_ip)); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + +#if LWIP_UDPLITE + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + u16_t chklen, chklen_hdr; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { + if (chklen != 0) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); + } + /* For UDP-Lite, checksum length of 0 means checksum + over the complete packet. (See RFC 3828 chap. 3.1) + At least the UDP-Lite header must be covered by the + checksum, therefore, if chksum_len has an illegal + value, we generate the checksum over the complete + packet to be safe. */ + chklen_hdr = 0; + chklen = q->tot_len; + } + udphdr->len = htons(chklen_hdr); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, + q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + acc = udphdr->chksum + (u16_t)~(chksum); + udphdr->chksum = FOLD_U32T(acc); + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) { + udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_GEN_UDP */ + + ip_proto = IP_PROTO_UDPLITE; + } else +#endif /* LWIP_UDPLITE */ + { /* UDP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + /* Checksum is mandatory over IPv6. */ + if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, + ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + ip_proto = IP_PROTO_UDP; + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a separate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); + q = NULL; + /* p is still referenced by the caller, and will live on */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. Use 0 to automatically bind + * to a random port between UDP_LOCAL_PORT_RANGE_START and + * UDP_LOCAL_PORT_RANGE_END. + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(ipaddr)); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + + /* By default, we don't allow to bind to a port that any other udp + PCB is alread bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { +#else /* SO_REUSE */ + /* port matches that of PCB in list and REUSEADDR not set -> reject */ + else { +#endif /* SO_REUSE */ + if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || + ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, + ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { + port = udp_new_port(); + if (port == 0) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + snmp_insert_udpidx_tree(pcb); + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port)); + return ERR_OK; +} + +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * ipaddr & port are expected to be in the same byte order as in the pcb. + * + * The udp pcb is bound to a random local port if not already bound. + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + struct netif *netif; + + if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", + ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); + } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + ipX_addr_set_any(0, &pcb->local_ip); + } + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +/** + * Disconnect a UDP PCB + * + * @param pcb the udp pcb to disconnect. + */ +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +/** + * Set a receive callback for a UDP PCB + * + * This callback will be called when receiving a datagram for the pcb. + * + * @param pcb the pcb for wich to set the recv callback + * @param recv function pointer of the callback function + * @param recv_arg additional argument to pass to the callback function + */ +void +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + + snmp_delete_udpidx_tree(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else { + for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + } + memp_free(MEMP_UDP_PCB, pcb); +} + +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) +{ + struct udp_pcb *pcb; + pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 + * which means checksum is generated over the whole datagram per default + * (recommended as default by RFC 3828). */ + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + return pcb; +} + +#if LWIP_IPV6 +/** + * Create a UDP PCB for IPv6. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip6(void) +{ + struct udp_pcb *pcb; + pcb = udp_new(); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/autoip.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/autoip.h new file mode 100644 index 00000000..e62b72e8 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/autoip.h @@ -0,0 +1,118 @@ +/** + * @file + * + * AutoIP Automatic LinkLocal IP Configuration + */ + +/* + * + * Copyright (c) 2007 Dominik Spies + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Dominik Spies + * + * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform + * with RFC 3927. + * + * + * Please coordinate changes and requests with Dominik Spies + * + */ + +#ifndef __LWIP_AUTOIP_H__ +#define __LWIP_AUTOIP_H__ + +#include "lwip/opt.h" + +#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "netif/etharp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* AutoIP Timing */ +#define AUTOIP_TMR_INTERVAL 100 +#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL) + +/* RFC 3927 Constants */ +#define PROBE_WAIT 1 /* second (initial random delay) */ +#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */ +#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */ +#define PROBE_NUM 3 /* (number of probe packets) */ +#define ANNOUNCE_NUM 2 /* (number of announcement packets) */ +#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ +#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ + +/* AutoIP client states */ +#define AUTOIP_STATE_OFF 0 +#define AUTOIP_STATE_PROBING 1 +#define AUTOIP_STATE_ANNOUNCING 2 +#define AUTOIP_STATE_BOUND 3 + +struct autoip +{ + ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */ + u8_t state; /* current AutoIP state machine state */ + u8_t sent_num; /* sent number of probes or announces, dependent on state */ + u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */ + u8_t lastconflict; /* ticks until a conflict can be solved by defending */ + u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */ +}; + + +#define autoip_init() /* Compatibility define, no init needed. */ + +/** Set a struct autoip allocated by the application to work with */ +void autoip_set_struct(struct netif *netif, struct autoip *autoip); + +/** Start AutoIP client */ +err_t autoip_start(struct netif *netif); + +/** Stop AutoIP client */ +err_t autoip_stop(struct netif *netif); + +/** Handles every incoming ARP Packet, called by etharp_arp_input */ +void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr); + +/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */ +void autoip_tmr(void); + +/** Handle a possible change in the network configuration */ +void autoip_network_changed(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_AUTOIP */ + +#endif /* __LWIP_AUTOIP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/icmp.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/icmp.h new file mode 100644 index 00000000..fa893b6b --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/icmp.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ICMP_H__ +#define __LWIP_ICMP_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP_ER 0 /* echo reply */ +#define ICMP_DUR 3 /* destination unreachable */ +#define ICMP_SQ 4 /* source quench */ +#define ICMP_RD 5 /* redirect */ +#define ICMP_ECHO 8 /* echo */ +#define ICMP_TE 11 /* time exceeded */ +#define ICMP_PP 12 /* parameter problem */ +#define ICMP_TS 13 /* timestamp */ +#define ICMP_TSR 14 /* timestamp reply */ +#define ICMP_IRQ 15 /* information request */ +#define ICMP_IR 16 /* information reply */ + +enum icmp_dur_type { + ICMP_DUR_NET = 0, /* net unreachable */ + ICMP_DUR_HOST = 1, /* host unreachable */ + ICMP_DUR_PROTO = 2, /* protocol unreachable */ + ICMP_DUR_PORT = 3, /* port unreachable */ + ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + ICMP_DUR_SR = 5 /* source route failed */ +}; + +enum icmp_te_type { + ICMP_TE_TTL = 0, /* time to live exceeded in transit */ + ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */ +}; + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +/** This is the standard ICMP header only that the u32_t data + * is splitted to two u16_t like ICMP echo needs it. + * This header is also used for other ICMP types that do not + * use the data part. + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t)) +#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c)) + + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +void icmp_input(struct pbuf *p, struct netif *inp); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#if (LWIP_IPV6 && LWIP_ICMP6) +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/igmp.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/igmp.h new file mode 100644 index 00000000..8aabac24 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/igmp.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2002 CITEL Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. +*/ + +#ifndef __LWIP_IGMP_H__ +#define __LWIP_IGMP_H__ + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* IGMP timer */ +#define IGMP_TMR_INTERVAL 100 /* Milliseconds */ +#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL) + +/* MAC Filter Actions, these are passed to a netif's + * igmp_mac_filter callback function. */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + + +/** + * igmp group structure - there is + * a list of groups for each interface + * these should really be linked from the interface, but + * if we keep them separate we will not affect the lwip original code + * too much + * + * There will be a group for the all systems group address but this + * will not run the state machine as it is used to kick off reports + * from all the other groups + */ +struct igmp_group { + /** next link */ + struct igmp_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting, negative is OFF */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/* Prototypes */ +void igmp_init(void); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest); +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +void igmp_tmr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/inet.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/inet.h new file mode 100644 index 00000000..7bff49b5 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/inet.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_H__ +#define __LWIP_INET_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +/** 255.255.255.255 */ +#define INADDR_NONE IPADDR_NONE +/** 127.0.0.1 */ +#define INADDR_LOOPBACK IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define INADDR_ANY IPADDR_ANY +/** 255.255.255.255 */ +#define INADDR_BROADCAST IPADDR_BROADCAST + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IN_CLASSA(a) IP_CLASSA(a) +#define IN_CLASSA_NET IP_CLASSA_NET +#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT +#define IN_CLASSA_HOST IP_CLASSA_HOST +#define IN_CLASSA_MAX IP_CLASSA_MAX + +#define IN_CLASSB(b) IP_CLASSB(b) +#define IN_CLASSB_NET IP_CLASSB_NET +#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT +#define IN_CLASSB_HOST IP_CLASSB_HOST +#define IN_CLASSB_MAX IP_CLASSB_MAX + +#define IN_CLASSC(c) IP_CLASSC(c) +#define IN_CLASSC_NET IP_CLASSC_NET +#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT +#define IN_CLASSC_HOST IP_CLASSC_HOST +#define IN_CLASSC_MAX IP_CLASSC_MAX + +#define IN_CLASSD(d) IP_CLASSD(d) +#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */ +#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */ +#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */ +#define IN_CLASSD_MAX IP_CLASSD_MAX + +#define IN_MULTICAST(a) IP_MULTICAST(a) + +#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a) +#define IN_BADCLASS(a) IP_BADCLASS(a) + +#define IN_LOOPBACKNET IP_LOOPBACKNET + +#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) +#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) +/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */ +#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr)) + +/* directly map this to the lwip internal functions */ +#define inet_addr(cp) ipaddr_addr(cp) +#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr) +#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr)) +#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4.h new file mode 100644 index 00000000..04b5b8a6 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_H__ +#define __LWIP_IP4_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the function ip_output_if_opt() is only used with IGMP */ +#define IP_OPTIONS_SEND LWIP_IGMP + +#define IP_HLEN 20 + +#define IP_PROTO_ICMP 1 +#define IP_PROTO_IGMP 2 +#define IP_PROTO_UDP 17 +#define IP_PROTO_UDPLITE 136 +#define IP_PROTO_TCP 6 + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length */ + PACK_STRUCT_FIELD(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FIELD(u8_t _tos); + /* total length */ + PACK_STRUCT_FIELD(u16_t _len); + /* identification */ + PACK_STRUCT_FIELD(u16_t _id); + /* fragment offset field */ + PACK_STRUCT_FIELD(u16_t _offset); +#define IP_RF 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* dont fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FIELD(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FIELD(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip_addr_p_t src); + PACK_STRUCT_FIELD(ip_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IPH_V(hdr) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#define IPH_LEN(hdr) ((hdr)->_len) +#define IPH_ID(hdr) ((hdr)->_id) +#define IPH_OFFSET(hdr) ((hdr)->_offset) +#define IPH_TTL(hdr) ((hdr)->_ttl) +#define IPH_PROTO(hdr) ((hdr)->_proto) +#define IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos) +#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto) +#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + + +#define ip_init() /* Compatibility define, no init needed. */ +struct netif *ip_route(ip_addr_t *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#endif /* IP_OPTIONS_SEND */ + +#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4_addr.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4_addr.h new file mode 100644 index 00000000..b05ae537 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip4_addr.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP4_ADDR_H__ +#define __LWIP_IP4_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the aligned version of ip_addr_t, + used as local variable, on the stack, etc. */ +struct ip_addr { + u32_t addr; +}; + +/* This is the packed version of ip_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr_packed { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip_addr_t as well as on ip_addr_p_t. */ +typedef struct ip_addr ip_addr_t; +typedef struct ip_addr_packed ip_addr_p_t; + +/* + * struct ipaddr2 is used in the definition of the ARP packet format in + * order to support compilers that don't have structure packing. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Forward declaration to not include netif.h */ +struct netif; + +extern const ip_addr_t ip_addr_any; +extern const ip_addr_t ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((u32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((u32_t)0x00000000UL) +/** 255.255.255.255 */ +#define IPADDR_BROADCAST ((u32_t)0xffffffffUL) + +/* Definitions of the bits in an Internet address integer. + + On subnets, host and network parts are found according to + the subnet mask, not these masks. */ +#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IP_CLASSA_NET 0xff000000 +#define IP_CLASSA_NSHIFT 24 +#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET) +#define IP_CLASSA_MAX 128 + +#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IP_CLASSB_NET 0xffff0000 +#define IP_CLASSB_NSHIFT 16 +#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET) +#define IP_CLASSB_MAX 65536 + +#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IP_CLASSC_NET 0xffffff00 +#define IP_CLASSC_NSHIFT 8 +#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET) + +#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IP_MULTICAST(a) IP_CLASSD(a) + +#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IP_LOOPBACKNET 127 /* official! */ + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IP address given by the four byte-parts */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IP address given by the four byte-parts. + Little-endian version that prevents the use of htonl. */ +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** MEMCPY-like copying of IP addresses where addresses are known to be + * 16-bit-aligned if the port is correctly configured (so a port could define + * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */ +#ifndef IPADDR2_COPY +#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t)) +#endif + +/** Copy IP address - faster than ip_addr_set: no NULL check */ +#define ip_addr_copy(dest, src) ((dest).addr = (src).addr) +/** Safely copy one IP address to another (src may be NULL) */ +#define ip_addr_set(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0 : \ + (src)->addr)) +/** Set complete address to zero */ +#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0) +/** Set address to IPADDR_ANY (no need for htonl()) */ +#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY) +/** Set address to loopback address */ +#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK)) +/** Safely copy one IP address to another and change byte order + * from host- to network-order. */ +#define ip_addr_set_hton(dest, src) ((dest)->addr = \ + ((src) == NULL ? 0:\ + htonl((src)->addr))) +/** IPv4 only: set the IP address given as an u32_t */ +#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32)) +/** IPv4 only: get the IP address as an u32_t */ +#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr) + +/** Get the network address by combining host address with netmask */ +#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr)) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY) + +#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif)) +u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif); + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \ + ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0)) + +/* Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0]) +#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1]) +#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2]) +#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3]) +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ipaddr_aton(const char *cp, ip_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr); +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip_frag.h b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip_frag.h new file mode 100644 index 00000000..47eca9f4 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv4/lwip/ip_frag.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * + */ + +#ifndef __LWIP_IP_FRAG_H__ +#define __LWIP_IP_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if IP_REASSEMBLY +/* The IP reassembly timer interval in milliseconds. */ +#define IP_TMR_INTERVAL 1000 + +/* IP reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip_reassdata { + struct ip_reassdata *next; + struct pbuf *p; + struct ip_hdr iphdr; + u16_t datagram_len; + u8_t flags; + u8_t timer; +}; + +void ip_reass_init(void); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ +#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/dhcp6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/dhcp6.h new file mode 100644 index 00000000..4b905c54 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_IP6_DHCP6_H__ +#define __LWIP_IP6_DHCP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*TODO: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* __LWIP_IP6_DHCP6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/ethip6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ethip6.h new file mode 100644 index 00000000..e7f412b4 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ETHIP6_H__ +#define __LWIP_ETHIP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* __LWIP_ETHIP6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/icmp6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/icmp6.h new file mode 100644 index 00000000..74bfdbe0 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/icmp6.h @@ -0,0 +1,152 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_ICMP6_H__ +#define __LWIP_ICMP6_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum icmp6_type { + ICMP6_TYPE_DUR = 1, /* Destination unreachable */ + ICMP6_TYPE_PTB = 2, /* Packet too big */ + ICMP6_TYPE_TE = 3, /* Time exceeded */ + ICMP6_TYPE_PP = 4, /* Parameter problem */ + ICMP6_TYPE_PE1 = 100, /* Private experimentation */ + ICMP6_TYPE_PE2 = 101, /* Private experimentation */ + ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */ + + ICMP6_TYPE_EREQ = 128, /* Echo request */ + ICMP6_TYPE_EREP = 129, /* Echo reply */ + ICMP6_TYPE_MLQ = 130, /* Multicast listener query */ + ICMP6_TYPE_MLR = 131, /* Multicast listener report */ + ICMP6_TYPE_MLD = 132, /* Multicast listener done */ + ICMP6_TYPE_RS = 133, /* Router solicitation */ + ICMP6_TYPE_RA = 134, /* Router advertisement */ + ICMP6_TYPE_NS = 135, /* Neighbor solicitation */ + ICMP6_TYPE_NA = 136, /* Neighbor advertisement */ + ICMP6_TYPE_RD = 137, /* Redirect */ + ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */ + ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */ + ICMP6_TYPE_MRT = 153, /* Multicast router termination */ + ICMP6_TYPE_PE3 = 200, /* Private experimentation */ + ICMP6_TYPE_PE4 = 201, /* Private experimentation */ + ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */ +}; + +enum icmp6_dur_code { + ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */ + ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */ + ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */ + ICMP6_DUR_ADDRESS = 3, /* Address unreachable */ + ICMP6_DUR_PORT = 4, /* Port unreachable */ + ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */ + ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */ +}; + +enum icmp6_te_code { + ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */ + ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */ +}; + +enum icmp6_pp_code { + ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */ + ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */ + ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */ +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +void icmp6_input(struct pbuf *p, struct netif *inp); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* __LWIP_ICMP6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/inet6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/inet6.h new file mode 100644 index 00000000..dbf98df0 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/inet6.h @@ -0,0 +1,92 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_INET6_H__ +#define __LWIP_INET6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in6_addr { + union { + u8_t u8_addr[16]; + u32_t u32_addr[4]; + } un; +#define s6_addr un.u32_addr +}; + +#define IN6ADDR_ANY_INIT {0,0,0,0} +#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)} + + +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_INET6_H__ */ + diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6.h new file mode 100644 index 00000000..b199c95a --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6.h @@ -0,0 +1,197 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_H__ +#define __LWIP_IP6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + + +/* The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /* version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /* payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* hop limit */ + PACK_STRUCT_FIELD(u8_t _hoplim); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(ip6_addr_p_t src); + PACK_STRUCT_FIELD(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* header length */ + PACK_STRUCT_FIELD(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FIELD(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FIELD(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FIELD(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FIELD(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FIELD(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FIELD(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (htonl(((v) << 28) | ((tc) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + + +#define ip6_init() /* TODO should we init current addresses and header pointer? */ +struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest); +ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ipX(netif, dest) (((netif) != NULL) ? \ + ip6_2_ipX(ip6_select_source_address(netif, dest)) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_addr.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_addr.h new file mode 100644 index 00000000..89b5b811 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_addr.h @@ -0,0 +1,286 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_ADDR_H__ +#define __LWIP_IP6_ADDR_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/* This is the packed version of ip6_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_addr_packed { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip6_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip6_addr_t as well as on ip6_addr_p_t. */ +typedef struct ip6_addr ip6_addr_t; +typedef struct ip6_addr_packed ip6_addr_p_t; + + +/** IP6_ADDR_ANY can be used as a fixed IPv6 address + * for the wildcard + */ +extern const ip6_addr_t ip6_addr_any; +#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any) + + + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IPv6 partial address given by byte-parts. */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IPv6 partial address given by byte-parts. +Little-endian version, stored in network order (no htonl). */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff) +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff) +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff) +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3])) & 0xffff) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : htonl((src)->addr[3]);}while(0) + + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \ + (((ip6addr)->addr[0] == 0) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == 0) && \ + ((ip6addr)->addr[3] == 0))) + + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* TODO define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = htonl(0xff000000UL | (htonl(if_id) & 0x00ffffffUL));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == htonl(0xff000000UL | (htonl((sn_addr)->addr[3]) & 0x00ffffffUL)))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x50 + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F, \ + ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_IP6_ADDR_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_frag.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_frag.h new file mode 100644 index 00000000..75898b8f --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/ip6_frag.h @@ -0,0 +1,102 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef __LWIP_IP6_FRAG_H__ +#define __LWIP_IP6_FRAG_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/* The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr * iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf * ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +/** A custom pbuf that holds a reference to another pbuf, which is freed + * when this custom pbuf is freed. This is used to create a custom PBUF_REF + * that points into the original pbuf. */ +#ifndef __LWIP_PBUF_CUSTOM_REF__ +#define __LWIP_PBUF_CUSTOM_REF__ +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* __LWIP_PBUF_CUSTOM_REF__ */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP6_FRAG_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/mld6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/mld6.h new file mode 100644 index 00000000..abd86e55 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/mld6.h @@ -0,0 +1,118 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_MLD6_H__ +#define __LWIP_MLD6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct mld_group { + /** next link */ + struct mld_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip6_addr_t group_address; + /** signifies we were the last person to report */ + u8_t last_reporter_flag; + /** current state of the group */ + u8_t group_state; + /** timer for reporting */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +/* MAC Filter Actions, these are passed to a netif's + * mld_mac_filter callback function. */ +#define MLD6_DEL_MAC_FILTER 0 +#define MLD6_ADD_MAC_FILTER 1 + + +#define mld6_init() /* TODO should we init tables? */ +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); +err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* __LWIP_MLD6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/ipv6/lwip/nd6.h b/external/badvpn_dns/lwip/src/include/ipv6/lwip/nd6.h new file mode 100644 index 00000000..28636e89 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/ipv6/lwip/nd6.h @@ -0,0 +1,369 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico Technologies Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef __LWIP_ND6_H__ +#define __LWIP_ND6_H__ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct for tables. */ +struct nd6_neighbor_cache_entry { + ip6_addr_t next_hop_address; + struct netif * netif; + u8_t lladdr[NETIF_MAX_HWADDR_LEN]; + /*u32_t pmtu;*/ +#if LWIP_ND6_QUEUEING + /** Pointer to queue of pending outgoing packets on this entry. */ + struct nd6_q_entry *q; +#else /* LWIP_ND6_QUEUEING */ + /** Pointer to a single pending outgoing packet on this entry. */ + struct pbuf *q; +#endif /* LWIP_ND6_QUEUEING */ + u8_t state; + u8_t isrouter; + union { + u32_t reachable_time; + u32_t delay_time; + u32_t probes_sent; + u32_t stale_time; + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u32_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif * netif; + u32_t invalidation_timer; +#if LWIP_IPV6_AUTOCONFIG + u8_t flags; +#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04 +#endif /* LWIP_IPV6_AUTOCONFIG */ +}; + +struct nd6_router_list_entry { + struct nd6_neighbor_cache_entry * neighbor_entry; + u32_t invalidation_timer; + u8_t flags; +}; + + +enum nd6_neighbor_cache_entry_state { + ND6_NO_ENTRY = 0, + ND6_INCOMPLETE, + ND6_REACHABLE, + ND6_STALE, + ND6_DELAY, + ND6_PROBE +}; + +#if LWIP_ND6_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct nd6_q_entry { + struct nd6_q_entry *next; + struct pbuf *p; +}; +#endif /* LWIP_ND6_QUEUEING */ + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u8_t reserved[3]); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u8_t current_hop_limit); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FIELD(ip6_addr_p_t target_address); + PACK_STRUCT_FIELD(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FIELD(u8_t reserved2[3]); + PACK_STRUCT_FIELD(u8_t site_prefix_length); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FIELD(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FIELD(u8_t type); + PACK_STRUCT_FIELD(u8_t length); + PACK_STRUCT_FIELD(u8_t prefix_length); + PACK_STRUCT_FIELD(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FIELD(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* the possible states of an IP address */ +#define IP6_ADDRESS_STATE_INVALID (0) +#define IP6_ADDRESS_STATE_VALID (0x4) +#define IP6_ADDRESS_STATE_PREFERRED (0x5) /* includes valid */ +#define IP6_ADDRESS_STATE_DEPRECATED (0x6) /* includes valid */ +#define IP6_ADDRESS_STATE_TENTATIV (0x8) + +/** 1 second period */ +#define ND6_TMR_INTERVAL 1000 + +/* Router tables. */ +/* TODO make these static? and entries accessible through API? */ +extern struct nd6_neighbor_cache_entry neighbor_cache[]; +extern struct nd6_destination_cache_entry destination_cache[]; +extern struct nd6_prefix_list_entry prefix_list[]; +extern struct nd6_router_list_entry default_router_list[]; + +/* Default values, can be updated by a RA message. */ +extern u32_t reachable_time; +extern u32_t retrans_timer; + +#define nd6_init() /* TODO should we init tables? */ +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif); +s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif); +u16_t nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif); +err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p); +#if LWIP_ND6_TCP_REACHABILITY_HINTS +void nd6_reachability_hint(ip6_addr_t * ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* __LWIP_ND6_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/api.h b/external/badvpn_dns/lwip/src/include/lwip/api.h new file mode 100644 index 00000000..ac58eebb --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/api.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_H__ +#define __LWIP_API_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/netbuf.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ + +/* Flags for netconn_write (u8_t) */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 +#define NETCONN_DONTBLOCK 0x04 + +/* Flags for struct netconn.flags (u8_t) */ +/** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores whether to wake up the original application task + if data couldn't be sent in the first try. */ +#define NETCONN_FLAG_WRITE_DELAYED 0x01 +/** Should this netconn avoid blocking? */ +#define NETCONN_FLAG_NON_BLOCKING 0x02 +/** Was the last connect action a non-blocking one? */ +#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04 +/** If this is set, a TCP netconn must call netconn_recved() to update + the TCP receive window (done automatically if not set). */ +#define NETCONN_FLAG_NO_AUTO_RECVED 0x08 +/** If a nonblocking write has been rejected before, poll_tcp needs to + check if the netconn is writable again */ +#define NETCONN_FLAG_CHECK_WRITESPACE 0x10 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ + +/** Protocol family and type of the netconn */ +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM = 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ +}; + +/** Current state of the netconn. Non-TCP netconns are always + * in state NETCONN_NONE! */ +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +/** Use to inform the callback function about changes */ +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS, + NETCONN_EVT_ERROR +}; + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +/* forward-declare some structs to avoid to include their headers */ +struct ip_pcb; +struct tcp_pcb; +struct udp_pcb; +struct raw_pcb; +struct netconn; +struct api_msg_msg; + +/** A callback prototype to inform about events for a netconn */ +typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len); + +/** A netconn descriptor */ +struct netconn { + /** type of the netconn (TCP, UDP or RAW) */ + enum netconn_type type; + /** current state of the netconn */ + enum netconn_state state; + /** the lwIP internal protocol control block */ + union { + struct ip_pcb *ip; + struct tcp_pcb *tcp; + struct udp_pcb *udp; + struct raw_pcb *raw; + } pcb; + /** the last error this netconn had */ + err_t last_err; + /** sem that is used to synchroneously execute functions in the core context */ + sys_sem_t op_completed; + /** mbox where received packets are stored until they are fetched + by the netconn application thread (can grow quite big) */ + sys_mbox_t recvmbox; +#if LWIP_TCP + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; +#endif /* LWIP_TCP */ + /** only used for socket layer */ +#if LWIP_SOCKET + int socket; +#endif /* LWIP_SOCKET */ +#if LWIP_SO_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVTIMEO + /** timeout to wait for new data to be received + (or connections to arrive for listening netconns) */ + int recv_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + /** maximum amount of bytes queued in recvmbox + not used for TCP: adjust TCP_WND instead! */ + int recv_bufsize; + /** number of bytes currently in recvmbox to be received, + tested against recv_bufsize to limit bytes on recvmbox + for UDP and RAW, used for FIONREAD */ + s16_t recv_avail; +#endif /* LWIP_SO_RCVBUF */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ + u8_t flags; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores how much is already sent. */ + size_t write_offset; + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. + Also used during connect and close. */ + struct api_msg_msg *current_msg; +#endif /* LWIP_TCP */ + /** A callback function that is informed about events for this netconn */ + netconn_callback callback; +}; + +/** Register an Network connection event */ +#define API_EVENT(c,e,l) if (c->callback) { \ + (*c->callback)(c, e, l); \ + } + +/** Set conn->last_err to err but don't overwrite fatal errors */ +#define NETCONN_SET_SAFE_ERR(conn, err) do { \ + SYS_ARCH_DECL_PROTECT(lev); \ + SYS_ARCH_PROTECT(lev); \ + if (!ERR_IS_FATAL((conn)->last_err)) { \ + (conn)->last_err = err; \ + } \ + SYS_ARCH_UNPROTECT(lev); \ +} while(0); + +/* Network connection functions: */ +#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL) +#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c) +struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr, + u16_t *port, u8_t local); +#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0) +#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1) + +err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port); +err_t netconn_disconnect (struct netconn *conn); +err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog); +#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG) +err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +void netconn_recved(struct netconn *conn, u32_t length); +err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + ip_addr_t *addr, u16_t port); +err_t netconn_send(struct netconn *conn, struct netbuf *buf); +err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, + ip_addr_t *netif_addr, enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + +#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\ + ERR_VAL) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ + +#define netconn_err(conn) ((conn)->last_err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +/** Set the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_set_nonblocking(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0) +/** Get the blocking status of netconn calls (@todo: write/send is missing) */ +#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0) + +/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_set_noautorecved(conn, val) do { if(val) { \ + (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \ +} else { \ + (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0) +/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */ +#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0) + +#if LWIP_SO_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO +/** Set the receive timeout in milliseconds */ +#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout)) +/** Get the receive timeout in milliseconds */ +#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout) +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF +/** Set the receive buffer in bytes */ +#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize)) +/** Get the receive buffer in bytes */ +#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize) +#endif /* LWIP_SO_RCVBUF*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/api_msg.h b/external/badvpn_dns/lwip/src/include/lwip/api_msg.h new file mode 100644 index 00000000..8268036a --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/api_msg.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_API_MSG_H__ +#define __LWIP_API_MSG_H__ + +#include "lwip/opt.h" + +#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/sys.h" +#include "lwip/igmp.h" +#include "lwip/api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For the netconn API, these values are use as a bitmask! */ +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR) + +/* IP addresses and port numbers are expected to be in + * the same byte order as in the corresponding pcb. + */ +/** This struct includes everything that is necessary to execute a function + for a netconn in another thread context (mainly used to process netconns + in the tcpip_thread context to be thread safe). */ +struct api_msg_msg { + /** The netconn which to process - always needed: it includes the semaphore + which is used to block the application thread until the function finished. */ + struct netconn *conn; + /** The return value of the function executed in tcpip_thread. */ + err_t err; + /** Depending on the executed function, one of these union members is used */ + union { + /** used for lwip_netconn_do_send */ + struct netbuf *b; + /** used for lwip_netconn_do_newconn */ + struct { + u8_t proto; + } n; + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ + struct { + ip_addr_t *ipaddr; + u16_t port; + } bc; + /** used for lwip_netconn_do_getaddr */ + struct { + ipX_addr_t *ipaddr; + u16_t *port; + u8_t local; + } ad; + /** used for lwip_netconn_do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } w; + /** used for lwip_netconn_do_recv */ + struct { + u32_t len; + } r; + /** used for lwip_netconn_do_close (/shutdown) */ + struct { + u8_t shut; + } sd; +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ + struct { + ipX_addr_t *multiaddr; + ipX_addr_t *netif_addr; + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +}; + +/** This struct contains a function to execute in another thread context and + a struct api_msg_msg that serves as an argument for this function. + This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */ +struct api_msg { + /** function to execute in tcpip_thread context */ + void (* function)(struct api_msg_msg *msg); + /** arguments for this function */ + struct api_msg_msg msg; +}; + +#if LWIP_DNS +/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn, + it has its own struct (to avoid struct api_msg getting bigger than necessary). + lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg + (see netconn_gethostbyname). */ +struct dns_api_msg { + /** Hostname to query or dotted IP address string */ + const char *name; + /** Rhe resolved address is stored here */ + ip_addr_t *addr; + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t *sem; + /** Errors are given back here */ + err_t *err; +}; +#endif /* LWIP_DNS */ + +void lwip_netconn_do_newconn ( struct api_msg_msg *msg); +void lwip_netconn_do_delconn ( struct api_msg_msg *msg); +void lwip_netconn_do_bind ( struct api_msg_msg *msg); +void lwip_netconn_do_connect ( struct api_msg_msg *msg); +void lwip_netconn_do_disconnect ( struct api_msg_msg *msg); +void lwip_netconn_do_listen ( struct api_msg_msg *msg); +void lwip_netconn_do_send ( struct api_msg_msg *msg); +void lwip_netconn_do_recv ( struct api_msg_msg *msg); +void lwip_netconn_do_write ( struct api_msg_msg *msg); +void lwip_netconn_do_getaddr ( struct api_msg_msg *msg); +void lwip_netconn_do_close ( struct api_msg_msg *msg); +void lwip_netconn_do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +void lwip_netconn_do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_MSG_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/arch.h b/external/badvpn_dns/lwip/src/include/lwip/arch.h new file mode 100644 index 00000000..4d6df773 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/arch.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ARCH_H__ +#define __LWIP_ARCH_H__ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#include "arch/cc.h" + +/** Temporary: define format string for size_t if not defined in cc.h */ +#ifndef SZT_F +#define SZT_F U32_F +#endif /* SZT_F */ +/** Temporary upgrade helper: define format string for u8_t as hex if not + defined in cc.h */ +#ifndef X8_F +#define X8_F "02x" +#endif /* X8_F */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PACK_STRUCT_BEGIN +#define PACK_STRUCT_BEGIN +#endif /* PACK_STRUCT_BEGIN */ + +#ifndef PACK_STRUCT_END +#define PACK_STRUCT_END +#endif /* PACK_STRUCT_END */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + + +#ifndef LWIP_UNUSED_ARG +#define LWIP_UNUSED_ARG(x) (void)x +#endif /* LWIP_UNUSED_ARG */ + + +#ifdef LWIP_PROVIDE_ERRNO + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/debug.h b/external/badvpn_dns/lwip/src/include/lwip/debug.h new file mode 100644 index 00000000..0fe04139 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/debug.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEBUG_H__ +#define __LWIP_DEBUG_H__ + +#include "lwip/arch.h" +#include "lwip/opt.h" + +/** lower two bits indicate debug level + * - 0 all + * - 1 warning + * - 2 serious + * - 3 severe + */ +#define LWIP_DBG_LEVEL_ALL 0x00 +#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */ +#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */ +#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */ +#define LWIP_DBG_LEVEL_SEVERE 0x03 +#define LWIP_DBG_MASK_LEVEL 0x03 + +/** flag for LWIP_DEBUGF to enable that debug message */ +#define LWIP_DBG_ON 0x80U +/** flag for LWIP_DEBUGF to disable that debug message */ +#define LWIP_DBG_OFF 0x00U + +/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */ +#define LWIP_DBG_TRACE 0x40U +/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */ +#define LWIP_DBG_STATE 0x20U +/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */ +#define LWIP_DBG_FRESH 0x10U +/** flag for LWIP_DEBUGF to halt after printing this debug message */ +#define LWIP_DBG_HALT 0x08U + +#ifndef LWIP_NOASSERT +#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \ + LWIP_PLATFORM_ASSERT(message); } while(0) +#else /* LWIP_NOASSERT */ +#define LWIP_ASSERT(message, assertion) +#endif /* LWIP_NOASSERT */ + +/** if "expression" isn't true, then print "message" and execute "handler" expression */ +#ifndef LWIP_ERROR +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + LWIP_PLATFORM_ASSERT(message); handler;}} while(0) +#endif /* LWIP_ERROR */ + +#ifdef LWIP_DEBUG +/** print debug message only if debug message type is enabled... + * AND is of correct type AND is at least LWIP_DBG_LEVEL + */ +#define LWIP_DEBUGF(debug, message) do { \ + if ( \ + ((debug) & LWIP_DBG_ON) && \ + ((debug) & LWIP_DBG_TYPES_ON) && \ + ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \ + LWIP_PLATFORM_DIAG(message); \ + if ((debug) & LWIP_DBG_HALT) { \ + while(1); \ + } \ + } \ + } while(0) + +#else /* LWIP_DEBUG */ +#define LWIP_DEBUGF(debug, message) +#endif /* LWIP_DEBUG */ + +#endif /* __LWIP_DEBUG_H__ */ + diff --git a/external/badvpn_dns/lwip/src/include/lwip/def.h b/external/badvpn_dns/lwip/src/include/lwip/def.h new file mode 100644 index 00000000..73a1b560 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/def.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_DEF_H__ +#define __LWIP_DEF_H__ + +/* arch.h might define NULL already */ +#include "lwip/arch.h" +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* Endianess-optimized shifting of two u8_t to create one u16_t */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define LWIP_MAKE_U16(a, b) ((a << 8) | b) +#else +#define LWIP_MAKE_U16(a, b) ((b << 8) | a) +#endif + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#ifndef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#define htons(x) lwip_htons(x) +#define ntohs(x) lwip_ntohs(x) +#define htonl(x) lwip_htonl(x) +#define ntohl(x) lwip_ntohl(x) +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ + +#if BYTE_ORDER == BIG_ENDIAN +#define lwip_htons(x) (x) +#define lwip_ntohs(x) (x) +#define lwip_htonl(x) (x) +#define lwip_ntohl(x) (x) +#define PP_HTONS(x) (x) +#define PP_NTOHS(x) (x) +#define PP_HTONL(x) (x) +#define PP_NTOHL(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#if LWIP_PLATFORM_BYTESWAP +#define lwip_htons(x) LWIP_PLATFORM_HTONS(x) +#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x) +#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x) +#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t lwip_htons(u16_t x); +u16_t lwip_ntohs(u16_t x); +u32_t lwip_htonl(u32_t x); +u32_t lwip_ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +/* These macros should be calculated by the preprocessor and are used + with compile-time constants only (so that there is no little-endian + overhead at runtime). */ +#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) +#define PP_NTOHS(x) PP_HTONS(x) +#define PP_HTONL(x) ((((x) & 0xff) << 24) | \ + (((x) & 0xff00) << 8) | \ + (((x) & 0xff0000UL) >> 8) | \ + (((x) & 0xff000000UL) >> 24)) +#define PP_NTOHL(x) PP_HTONL(x) + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_DEF_H__ */ + diff --git a/external/badvpn_dns/lwip/src/include/lwip/dhcp.h b/external/badvpn_dns/lwip/src/include/lwip/dhcp.h new file mode 100644 index 00000000..32d93381 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/dhcp.h @@ -0,0 +1,242 @@ +/** @file + */ + +#ifndef __LWIP_DHCP_H__ +#define __LWIP_DHCP_H__ + +#include "lwip/opt.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** period (in seconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_SECS 60 +/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */ +#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL) +/** period (in milliseconds) of the application calling dhcp_fine_tmr() */ +#define DHCP_FINE_TIMER_MSECS 500 + +#define DHCP_CHADDR_LEN 16U +#define DHCP_SNAME_LEN 64U +#define DHCP_FILE_LEN 128U + +struct dhcp +{ + /** transaction identifier of last sent request */ + u32_t xid; + /** our connection to the DHCP server */ + struct udp_pcb *pcb; + /** incoming msg */ + struct dhcp_msg *msg_in; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif + u8_t subnet_mask_given; + + struct pbuf *p_out; /* pbuf of outcoming msg */ + struct dhcp_msg *msg_out; /* outgoing msg */ + u16_t options_out_len; /* outgoing msg options length */ + u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */ + u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */ + u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */ + ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */ + ip_addr_t offered_ip_addr; + ip_addr_t offered_sn_mask; + ip_addr_t offered_gw_addr; + + u32_t offered_t0_lease; /* lease period (in seconds) */ + u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */ + u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */ + /* @todo: LWIP_DHCP_BOOTP_FILE configuration option? + integrate with possible TFTP-client for booting? */ +#if LWIP_DHCP_BOOTP_FILE + ip_addr_t offered_si_addr; + char boot_file_name[DHCP_FILE_LEN]; +#endif /* LWIP_DHCP_BOOTPFILE */ +}; + +/* MUST be compiled with "pack structs" or equivalent! */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** minimum set of fields of any DHCP message */ +struct dhcp_msg +{ + PACK_STRUCT_FIELD(u8_t op); + PACK_STRUCT_FIELD(u8_t htype); + PACK_STRUCT_FIELD(u8_t hlen); + PACK_STRUCT_FIELD(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FIELD(ip_addr_p_t ciaddr); + PACK_STRUCT_FIELD(ip_addr_p_t yiaddr); + PACK_STRUCT_FIELD(ip_addr_p_t siaddr); + PACK_STRUCT_FIELD(ip_addr_p_t giaddr); + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]); + PACK_STRUCT_FIELD(u32_t cookie); +#define DHCP_MIN_OPTIONS_LEN 68U +/** make sure user does not configure this too small */ +#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN)) +# undef DHCP_OPTIONS_LEN +#endif +/** allow this to be configured in lwipopts.h, but not too small */ +#if (!defined(DHCP_OPTIONS_LEN)) +/** set this to be sufficient for your options in outgoing DHCP msgs */ +# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN +#endif + PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp); +/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */ +#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0) +void dhcp_cleanup(struct netif *netif); +/** start DHCP configuration */ +err_t dhcp_start(struct netif *netif); +/** enforce early lease renewal (not needed normally)*/ +err_t dhcp_renew(struct netif *netif); +/** release the DHCP lease, usually called before dhcp_stop()*/ +err_t dhcp_release(struct netif *netif); +/** stop DHCP configuration */ +void dhcp_stop(struct netif *netif); +/** inform server of our manual IP address */ +void dhcp_inform(struct netif *netif); +/** Handle a possible change in the network configuration */ +void dhcp_network_changed(struct netif *netif); + +/** if enabled, check whether the offered IP address is not in use, using ARP */ +#if DHCP_DOES_ARP_CHECK +void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr); +#endif + +/** to be called every minute */ +void dhcp_coarse_tmr(void); +/** to be called every half second */ +void dhcp_fine_tmr(void); + +/** DHCP message item offsets and length */ +#define DHCP_OP_OFS 0 +#define DHCP_HTYPE_OFS 1 +#define DHCP_HLEN_OFS 2 +#define DHCP_HOPS_OFS 3 +#define DHCP_XID_OFS 4 +#define DHCP_SECS_OFS 8 +#define DHCP_FLAGS_OFS 10 +#define DHCP_CIADDR_OFS 12 +#define DHCP_YIADDR_OFS 16 +#define DHCP_SIADDR_OFS 20 +#define DHCP_GIADDR_OFS 24 +#define DHCP_CHADDR_OFS 28 +#define DHCP_SNAME_OFS 44 +#define DHCP_FILE_OFS 108 +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS DHCP_MSG_LEN +#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#define DHCP_OFF 0 +#define DHCP_REQUESTING 1 +#define DHCP_INIT 2 +#define DHCP_REBOOTING 3 +#define DHCP_REBINDING 4 +#define DHCP_RENEWING 5 +#define DHCP_SELECTING 6 +#define DHCP_INFORMING 7 +#define DHCP_CHECKING 8 +#define DHCP_PERMANENT 9 +#define DHCP_BOUND 10 +/** not yet implemented #define DHCP_RELEASING 11 */ +#define DHCP_BACKING_OFF 12 + +/** AUTOIP cooperatation flags */ +#define DHCP_AUTOIP_COOP_STATE_OFF 0 +#define DHCP_AUTOIP_COOP_STATE_ON 1 + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/** DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/** BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_END 255 + +/** DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/** possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DHCP */ + +#endif /*__LWIP_DHCP_H__*/ diff --git a/external/badvpn_dns/lwip/src/include/lwip/dns.h b/external/badvpn_dns/lwip/src/include/lwip/dns.h new file mode 100644 index 00000000..6c7d9b07 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/dns.h @@ -0,0 +1,124 @@ +/** + * lwip DNS resolver header file. + + * Author: Jim Pettinato + * April 2007 + + * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LWIP_DNS_H__ +#define __LWIP_DNS_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** DNS timer period */ +#define DNS_TMR_INTERVAL 1000 + +/** DNS field TYPE used for "Resource Records" */ +#define DNS_RRTYPE_A 1 /* a host address */ +#define DNS_RRTYPE_NS 2 /* an authoritative name server */ +#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ +#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ +#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ +#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ +#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ +#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ +#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ +#define DNS_RRTYPE_WKS 11 /* a well known service description */ +#define DNS_RRTYPE_PTR 12 /* a domain name pointer */ +#define DNS_RRTYPE_HINFO 13 /* host information */ +#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ +#define DNS_RRTYPE_MX 15 /* mail exchange */ +#define DNS_RRTYPE_TXT 16 /* text strings */ + +/** DNS field CLASS used for "Resource Records" */ +#define DNS_RRCLASS_IN 1 /* the Internet */ +#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ +#define DNS_RRCLASS_CH 3 /* the CHAOS class */ +#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ +#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ + +/* The size used for the next line is rather a hack, but it prevents including socket.h in all files + that include memp.h, and that would possibly break portability (since socket.h defines some types + and constants possibly already define by the OS). + Calculation rule: + sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */ +#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1) + +#if DNS_LOCAL_HOSTLIST +/** struct used for local host-list */ +struct local_hostlist_entry { + /** static hostname */ + const char *name; + /** static host address in network byteorder */ + ip_addr_t addr; + struct local_hostlist_entry *next; +}; +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN +#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH +#endif +#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1)) +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#endif /* DNS_LOCAL_HOSTLIST */ + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); + +void dns_init(void); +void dns_tmr(void); +void dns_setserver(u8_t numdns, ip_addr_t *dnsserver); +ip_addr_t dns_getserver(u8_t numdns); +err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr, + dns_found_callback found, void *callback_arg); + +#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +int dns_local_removehost(const char *hostname, const ip_addr_t *addr); +err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/err.h b/external/badvpn_dns/lwip/src/include/lwip/err.h new file mode 100644 index 00000000..ac907729 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/err.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_ERR_H__ +#define __LWIP_ERR_H__ + +#include "lwip/opt.h" +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define LWIP_ERR_T in cc.h if you want to use + * a different type for your platform (must be signed). */ +#ifdef LWIP_ERR_T +typedef LWIP_ERR_T err_t; +#else /* LWIP_ERR_T */ +typedef s8_t err_t; +#endif /* LWIP_ERR_T*/ + +/* Definitions for error constants. */ + +#define ERR_OK 0 /* No error, everything OK. */ +#define ERR_MEM -1 /* Out of memory error. */ +#define ERR_BUF -2 /* Buffer error. */ +#define ERR_TIMEOUT -3 /* Timeout. */ +#define ERR_RTE -4 /* Routing problem. */ +#define ERR_INPROGRESS -5 /* Operation in progress */ +#define ERR_VAL -6 /* Illegal value. */ +#define ERR_WOULDBLOCK -7 /* Operation would block. */ +#define ERR_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) + +#define ERR_ABRT -10 /* Connection aborted. */ +#define ERR_RST -11 /* Connection reset. */ +#define ERR_CLSD -12 /* Connection closed. */ +#define ERR_CONN -13 /* Not connected. */ + +#define ERR_ARG -14 /* Illegal argument. */ + +#define ERR_IF -15 /* Low-level netif error */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/inet_chksum.h b/external/badvpn_dns/lwip/src/include/lwip/inet_chksum.h new file mode 100644 index 00000000..e1687888 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/inet_chksum.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INET_CHKSUM_H__ +#define __LWIP_INET_CHKSUM_H__ + +#include "lwip/opt.h" + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +/** Swap the bytes in an u16_t: much like htons() for little-endian */ +#ifndef SWAP_BYTES_IN_WORD +#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) +/* little endian and PLATFORM_BYTESWAP defined */ +#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w) +#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */ +/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */ +#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8) +#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/ +#endif /* SWAP_BYTES_IN_WORD */ + +/** Split an u32_t in two u16_ts and add them up */ +#ifndef FOLD_U32T +#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL)) +#endif + +#if LWIP_CHECKSUM_ON_COPY +/** Function-like macro: same as MEMCPY but returns the checksum of copied data + as u16_t */ +#ifndef LWIP_CHKSUM_COPY +#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len) +#ifndef LWIP_CHKSUM_COPY_ALGORITHM +#define LWIP_CHKSUM_COPY_ALGORITHM 1 +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ +#endif /* LWIP_CHKSUM_COPY */ +#else /* LWIP_CHECKSUM_ON_COPY */ +#define LWIP_CHKSUM_COPY_ALGORITHM 0 +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest); + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest))) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest))) + +#else /* LWIP_IPV6 */ + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + inet_chksum_pseudo(p, proto, proto_len, src, dest) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, dest) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/external/badvpn_dns/lwip/src/include/lwip/init.h b/external/badvpn_dns/lwip/src/include/lwip/init.h new file mode 100644 index 00000000..4e2e2856 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_INIT_H__ +#define __LWIP_INIT_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** X.x.x: Major version of the stack */ +#define LWIP_VERSION_MAJOR 1U +/** x.X.x: Minor version of the stack */ +#define LWIP_VERSION_MINOR 4U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 1U +/** For release candidates, this is set to 1..254 + * For official releases, this is set to 255 (LWIP_RC_RELEASE) + * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */ +#define LWIP_VERSION_RC 0U + +/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */ +#define LWIP_RC_RELEASE 255U +/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */ +#define LWIP_RC_DEVELOPMENT 0U + +#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE) +#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT) +#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT)) + +/** Provides the version of the stack */ +#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \ + LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC) + +/* Modules initialization */ +void lwip_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/ip.h b/external/badvpn_dns/lwip/src/include/lwip/ip.h new file mode 100644 index 00000000..a0cd1d4d --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/ip.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_H__ +#define __LWIP_IP_H__ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/netif.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is passed as the destination address to ip_output_if (not + to ip_output), meaning that an IP header already is constructed + in the pbuf. This is used when TCP retransmits. */ +#ifdef IP_HDRINCL +#undef IP_HDRINCL +#endif /* IP_HDRINCL */ +#define IP_HDRINCL NULL + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +#if LWIP_IPV6 +#define IP_PCB_ISIPV6_MEMBER u8_t isipv6; +#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6) +#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \ + ((pcb)->isipv6 != 0) : \ + ((pcb)->isipv6 == 0)) +#define PCB_ISIPV6(pcb) ((pcb)->isipv6) +#else +#define IP_PCB_ISIPV6_MEMBER +#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1 +#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1 +#define PCB_ISIPV6(pcb) 0 +#endif /* LWIP_IPV6 */ + +/* This is the common part of all PCB types. It needs to be at the + beginning of a PCB type definition. It is located here so that + changes to this common part are made in one location instead of + having to change all PCB structs. */ +#define IP_PCB \ + IP_PCB_ISIPV6_MEMBER \ + /* ip addresses in network byte order */ \ + ipX_addr_t local_ip; \ + ipX_addr_t remote_ip; \ + /* Socket options */ \ + u8_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + +struct ip_pcb { +/* Common members of all PCB types */ + IP_PCB; +}; + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER 0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/) + +/* Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** The interface that provided the packet for the current callback invocation. */ + struct netif *current_netif; + /** Header of the input packet currently being processed. */ + const struct ip_hdr *current_ip4_header; +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + const struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ipX_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ipX_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** Get the interface that received the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_netif() (ip_data.current_netif) +/** Get the IP header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip_current_header() (ip_data.current_ip4_header) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV6 +/** Get the IPv6 header of the current packet. + * This function must only be called from a receive callback (udp_recv, + * raw_recv, tcp_accept). It will return NULL otherwise. */ +#define ip6_current_header() (ip_data.current_ip6_header) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ipX_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip_current_header())) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \ + (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len())) + +/** Set an IP_PCB to IPv6 (IPv4 is the default) */ +#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0) + +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest)) + +#else /* LWIP_IPV6 */ + +/** Always returns FALSE when only supporting IPv4 */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip_current_header()) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if LWIP_IPV6 +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ((isipv6) ? \ + ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \ + ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto)) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ((isipv6) ? \ + ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint)) +#define ipX_route(isipv6, src, dest) \ + ((isipv6) ? \ + ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \ + ip_route(ipX_2_ip(dest))) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ((isipv6) ? \ + ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \ + ip_netif_get_local_ipX(netif)) +#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p)) +#else /* LWIP_IPV6 */ +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ip_output(p, src, dest, ttl, tos, proto) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ipX_route(isipv6, src, dest) \ + ip_route(ipX_2_ip(dest)) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ip_netif_get_local_ipX(netif) +#define ipX_debug_print(is_ipv6, p) ip_debug_print(p) +#endif /* LWIP_IPV6 */ + +#define ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \ + (netif) = ipX_route(isipv6, src, dest); \ + (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/external/badvpn_dns/lwip/src/include/lwip/ip_addr.h b/external/badvpn_dns/lwip/src/include/lwip/ip_addr.h new file mode 100644 index 00000000..7bd03cbd --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/ip_addr.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_IP_ADDR_H__ +#define __LWIP_IP_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV6 +/* A union struct for both IP version's addresses. */ +typedef union { + ip_addr_t ip4; + ip6_addr_t ip6; +} ipX_addr_t; + +/** These functions only exist for type-safe conversion from ip_addr_t to + ip6_addr_t and back */ +#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER +static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr) +{ return (ip6_addr_t*)ipaddr;} +static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr) +{ return (ip_addr_t*)ip6addr; } +static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr) +{ return (ipX_addr_t*)ipaddr; } +static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr) +{ return (ipX_addr_t*)ip6addr; } +#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */ +#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr)) +#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr)) +#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr) +#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr) +#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/ +#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6)) +#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4)) + +#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_copy((dest).ip6, (src).ip6); }else{ \ + ip_addr_copy((dest).ip4, (src).ip4); }}while(0) +#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), src); }}while(0) +#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \ + ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \ + ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \ + ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2))) +#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_isany(ipX_2_ip6(ipaddr)) : \ + ip_addr_isany(ipX_2_ip(ipaddr))) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \ + ip_addr_ismulticast(ipX_2_ip(ipaddr))) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \ + ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \ + ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0) + +#else /* LWIP_IPV6 */ + +typedef ip_addr_t ipX_addr_t; +#define ipX_2_ip(ipaddr) (ipaddr) +#define ip_2_ipX(ipaddr) (ipaddr) + +#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src) +#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr) +#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr) +#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2) +#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/mem.h b/external/badvpn_dns/lwip/src/include/lwip/mem.h new file mode 100644 index 00000000..5bb906b6 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/mem.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +/* aliases for C library malloc() */ +#define mem_init() +/* in case C library malloc() needs extra protection, + * allow these defines to be overridden. + */ +#ifndef mem_free +#define mem_free free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +/* Since there is no C library allocation function to shrink memory without + moving it, define this to nothing. */ +#ifndef mem_trim +#define mem_trim(mem, size) (mem) +#endif +#else /* MEM_LIBC_MALLOC */ + +/* MEM_SIZE would have to be aligned, but using 64000 here instead of + * 65535 leaves some room for alignment... + */ +#if MEM_SIZE > 64000L +typedef u32_t mem_size_t; +#define MEM_SIZE_F U32_F +#else +typedef u16_t mem_size_t; +#define MEM_SIZE_F U16_F +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_trim is not used when using pools instead of a heap: + we can't free part of a pool element and don't want to copy the rest */ +#define mem_trim(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_trim(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ + +/** Calculate memory size for an aligned buffer - returns the next highest + * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and + * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). + */ +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +/** Calculate safe memory size for an aligned buffer when using an unaligned + * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the + * start (e.g. if buffer is u8_t[] and actual data will be u32_t*) + */ +#ifndef LWIP_MEM_ALIGN_BUFFER +#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1)) +#endif + +/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT + * so that ADDR % MEM_ALIGNMENT == 0 + */ +#ifndef LWIP_MEM_ALIGN +#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1))) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEM_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/memp.h b/external/badvpn_dns/lwip/src/include/lwip/memp.h new file mode 100644 index 00000000..f0d07399 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/memp.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_MEMP_H__ +#define __LWIP_MEMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */ +typedef enum { +#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, +#include "lwip/memp_std.h" + MEMP_MAX +} memp_t; + +#if MEM_USE_POOLS +/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */ +typedef enum { + /* Get the first (via: + MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/ + MEMP_POOL_HELPER_FIRST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START 1 +#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0 +#define LWIP_MALLOC_MEMPOOL_END +#include "lwip/memp_std.h" + ) , + /* Get the last (via: + MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */ + MEMP_POOL_HELPER_LAST = ((u8_t) +#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size * +#define LWIP_MALLOC_MEMPOOL_END 1 +#include "lwip/memp_std.h" + ) +} memp_pool_helper_t; + +/* The actual start and stop values are here (cast them over) + We use this helper type and these defines so we can avoid using const memp_t values */ +#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST) +#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST) +#endif /* MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC || MEM_USE_POOLS +extern const u16_t memp_sizes[MEMP_MAX]; +#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */ + +#if MEMP_MEM_MALLOC + +#include "mem.h" + +#define memp_init() +#define memp_malloc(type) mem_malloc(memp_sizes[type]) +#define memp_free(type, mem) mem_free(mem) + +#else /* MEMP_MEM_MALLOC */ + +#if MEM_USE_POOLS +/** This structure is used to save the pool one element came from. */ +struct memp_malloc_helper +{ + memp_t poolnr; +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/memp_std.h b/external/badvpn_dns/lwip/src/include/lwip/memp_std.h new file mode 100644 index 00000000..592a2824 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/memp_std.h @@ -0,0 +1,135 @@ +/* + * SETUP: Make sure we define everything we will need. + * + * We have create three types of pools: + * 1) MEMPOOL - standard pools + * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c + * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct + * + * If the include'r doesn't require any special treatment of each of the types + * above, then will declare #2 & #3 to be just standard mempools. + */ +#ifndef LWIP_MALLOC_MEMPOOL +/* This treats "malloc pools" just like any other pool. + The pools are a little bigger to provide 'size' as the amount of user data. */ +#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size) +#define LWIP_MALLOC_MEMPOOL_START +#define LWIP_MALLOC_MEMPOOL_END +#endif /* LWIP_MALLOC_MEMPOOL */ + +#ifndef LWIP_PBUF_MEMPOOL +/* This treats "pbuf pools" just like any other pool. + * Allocates buffers for a pbuf struct AND a payload size */ +#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#endif /* LWIP_PBUF_MEMPOOL */ + + +/* + * A list of internal pools used by LWIP. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ +#if LWIP_RAW +LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ +#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") +#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +#if LWIP_NETCONN +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if !LWIP_TCPIP_CORE_LOCKING_INPUT +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */ +#endif /* NO_SYS==0 */ + +#if LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */ +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* LWIP_TIMERS */ + +#if LWIP_SNMP +LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE") +LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE") +LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND") +LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE") +#endif /* LWIP_SNMP */ +#if LWIP_DNS && LWIP_SOCKET +LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB") +#endif /* LWIP_DNS && LWIP_SOCKET */ +#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC +LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST") +#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ +#if PPP_SUPPORT && PPPOE_SUPPORT +LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ + +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + +/* + * A list of pools of pbuf's used by LWIP. + * + * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + * This allocates enough space for the pbuf struct and a payload. + * (Example: pbuf_payload_size=0 allocates only size for the struct) + */ +LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * Allow for user-defined pools; this must be explicitly set in lwipopts.h + * since the default is to NOT look for lwippools.h + */ +#if MEMP_USE_CUSTOM_POOLS +#include "lwippools.h" +#endif /* MEMP_USE_CUSTOM_POOLS */ + +/* + * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later + * (#undef is ignored for something that is not defined) + */ +#undef LWIP_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL +#undef LWIP_MALLOC_MEMPOOL_START +#undef LWIP_MALLOC_MEMPOOL_END +#undef LWIP_PBUF_MEMPOOL diff --git a/external/badvpn_dns/lwip/src/include/lwip/netbuf.h b/external/badvpn_dns/lwip/src/include/lwip/netbuf.h new file mode 100644 index 00000000..d12fe270 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/netbuf.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This netbuf has dest-addr/port set */ +#define NETBUF_FLAG_DESTADDR 0x01 +/** This netbuf includes a checksum */ +#define NETBUF_FLAG_CHKSUM 0x02 + +struct netbuf { + struct pbuf *p, *ptr; + ipX_addr_t addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY +#if LWIP_CHECKSUM_ON_COPY + u8_t flags; +#endif /* LWIP_CHECKSUM_ON_COPY */ + u16_t toport_chksum; +#if LWIP_NETBUF_RECVINFO + ipX_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#define netbuf_copy_partial(buf, dataptr, len, offset) \ + pbuf_copy_partial((buf)->p, (dataptr), (len), (offset)) +#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0) +#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len) +#define netbuf_len(buf) ((buf)->p->tot_len) +#define netbuf_fromaddr(buf) (ipX_2_ip(&((buf)->addr))) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (ipX_2_ip(&((buf)->toaddr))) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), destaddr) +#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0) +#endif /* LWIP_NETBUF_RECVINFO */ +#if LWIP_CHECKSUM_ON_COPY +#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \ + (buf)->toport_chksum = chksum; } while(0) +#endif /* LWIP_CHECKSUM_ON_COPY */ + +#if LWIP_IPV6 +#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr))) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr) +#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr))) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr) +#endif /* LWIP_IPV6 */ + +#define netbuf_fromaddr_ipX(buf) (&((buf)->addr)) +#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr)) + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/netdb.h b/external/badvpn_dns/lwip/src/include/lwip/netdb.h new file mode 100644 index 00000000..7587e2f2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/netdb.h @@ -0,0 +1,124 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIP_NETDB_H__ +#define __LWIP_NETDB_H__ + +#include "lwip/opt.h" + +#if LWIP_DNS && LWIP_SOCKET + +#include /* for size_t */ + +#include "lwip/inet.h" +#include "lwip/sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* some rarely used options */ +#ifndef LWIP_DNS_API_DECLARE_H_ERRNO +#define LWIP_DNS_API_DECLARE_H_ERRNO 1 +#endif + +#ifndef LWIP_DNS_API_DEFINE_ERRORS +#define LWIP_DNS_API_DEFINE_ERRORS 1 +#endif + +#ifndef LWIP_DNS_API_DECLARE_STRUCTS +#define LWIP_DNS_API_DECLARE_STRUCTS 1 +#endif + +#if LWIP_DNS_API_DEFINE_ERRORS +/** Errors used by the DNS API functions, h_errno can be one of them */ +#define EAI_NONAME 200 +#define EAI_SERVICE 201 +#define EAI_FAIL 202 +#define EAI_MEMORY 203 + +#define HOST_NOT_FOUND 210 +#define NO_DATA 211 +#define NO_RECOVERY 212 +#define TRY_AGAIN 213 +#endif /* LWIP_DNS_API_DEFINE_ERRORS */ + +#if LWIP_DNS_API_DECLARE_STRUCTS +struct hostent { + char *h_name; /* Official name of the host. */ + char **h_aliases; /* A pointer to an array of pointers to alternative host names, + terminated by a null pointer. */ + int h_addrtype; /* Address type. */ + int h_length; /* The length, in bytes, of the address. */ + char **h_addr_list; /* A pointer to an array of pointers to network addresses (in + network byte order) for the host, terminated by a null pointer. */ +#define h_addr h_addr_list[0] /* for backward compatibility */ +}; + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Address family of socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol of socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address of socket. */ + char *ai_canonname; /* Canonical name of service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; +#endif /* LWIP_DNS_API_DECLARE_STRUCTS */ + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessable error code set by the DNS API functions */ +extern int h_errno; +#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/ + +struct hostent *lwip_gethostbyname(const char *name); +int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf, + size_t buflen, struct hostent **result, int *h_errnop); +void lwip_freeaddrinfo(struct addrinfo *ai); +int lwip_getaddrinfo(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct addrinfo **res); + +#if LWIP_COMPAT_SOCKETS +#define gethostbyname(name) lwip_gethostbyname(name) +#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \ + lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop) +#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo) +#define getaddrinfo(nodname, servname, hints, res) \ + lwip_getaddrinfo(nodname, servname, hints, res) +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_DNS && LWIP_SOCKET */ + +#endif /* __LWIP_NETDB_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/netif.h b/external/badvpn_dns/lwip/src/include/lwip/netif.h new file mode 100644 index 00000000..f9770324 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/netif.h @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_NETIF_H__ +#define __LWIP_NETIF_H__ + +#include "lwip/opt.h" + +#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF) + +#include "lwip/err.h" + +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Throughout this file, IP addresses are expected to be in + * the same byte order as in IP_PCB. */ + +/** must be the maximum of all used hardware address lengths + across all types of interfaces in use */ +#define NETIF_MAX_HWADDR_LEN 6U + +/** Whether the network interface is 'up'. This is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + * It is set by the startup code (for static IP configuration) or + * by dhcp/autoip when an address has been assigned. + */ +#define NETIF_FLAG_UP 0x01U +/** If set, the netif has broadcast capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_BROADCAST 0x02U +/** If set, the netif is one end of a point-to-point connection. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** If set, the interface is configured using DHCP. + * Set by the DHCP code when starting or stopping DHCP. */ +#define NETIF_FLAG_DHCP 0x08U +/** If set, the interface has an active link + * (set by the network interface driver). + * Either set by the netif driver in its init function (if the link + * is up at that time) or at a later point once the link comes up + * (if link detection is supported by the hardware). */ +#define NETIF_FLAG_LINK_UP 0x10U +/** If set, the netif is an ethernet device using ARP. + * Set by the netif driver in its init function. + * Used to check input packet types and use of DHCP. */ +#define NETIF_FLAG_ETHARP 0x20U +/** If set, the netif is an ethernet device. It might not use + * ARP or TCP/IP if it is used for PPPoE only. + */ +#define NETIF_FLAG_ETHERNET 0x40U +/** If set, the netif has IGMP capability. + * Set by the netif driver in its init function. */ +#define NETIF_FLAG_IGMP 0x80U +/** Whether to pretend that we are every host for TCP packets. + * Set by netif_set_pretend_tcp. */ +#define NETIF_FLAG_PRETEND_TCP 0x100U + +/** Function prototype for netif init functions. Set up flags and output/linkoutput + * callback functions in this function. + * + * @param netif The netif to initialize + */ +typedef err_t (*netif_init_fn)(struct netif *netif); +/** Function prototype for netif->input functions. This function is saved as 'input' + * callback function in the netif struct. Call it when a packet has been received. + * + * @param p The received packet, copied into a pbuf + * @param inp The netif which received the packet + */ +typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp); +/** Function prototype for netif->output functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'etharp_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IP address to which the packet shall be sent + */ +typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr); +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'nd_output' and set + * 'linkoutput'. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (p->payload points to IP header) + * @param ipaddr The IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ +/** Function prototype for netif->linkoutput functions. Only used for ethernet + * netifs. This function is called by ARP when a packet shall be sent. + * + * @param netif The netif which shall send a packet + * @param p The packet to send (raw ethernet packet) + */ +typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p); +/** Function prototype for netif status- or link-callback functions. */ +typedef void (*netif_status_callback_fn)(struct netif *netif); +/** Function prototype for netif igmp_mac_filter functions */ +typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif, + ip_addr_t *group, u8_t action); +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + ip6_addr_t *group, u8_t action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +/** Generic data structure used for all lwIP network interfaces. + * The following fields should be filled in by the initialization + * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */ +struct netif { + /** pointer to next in linked list */ + struct netif *next; + + /** IP address configuration in network byte order */ + ip_addr_t ip_addr; + ip_addr_t netmask; + ip_addr_t gw; + +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + netif_input_fn input; + /** This function is called by the IP module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_fn output; + /** This function is called by the ARP module when it wants + * to send a packet on the interface. This function outputs + * the pbuf as-is on the link medium. */ + netif_linkoutput_fn linkoutput; +#if LWIP_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + netif_status_callback_fn status_callback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + netif_status_callback_fn link_callback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + /** This field can be set by the device driver and could point + * to state information for the device. */ + void *state; +#if LWIP_DHCP + /** the DHCP client state information for this netif */ + struct dhcp *dhcp; +#endif /* LWIP_DHCP */ +#if LWIP_AUTOIP + /** the AutoIP client state information for this netif */ + struct autoip *autoip; +#endif +#if LWIP_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /** the DHCPv6 client state information for this netif */ + struct dhcp6 *dhcp6; +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_NETIF_HOSTNAME + /* the hostname for this netif, NULL is a valid value */ + char* hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + /** maximum transfer unit (in bytes) */ + u16_t mtu; + /** number of bytes used in hwaddr */ + u8_t hwaddr_len; + /** link level hardware address of this interface */ + u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; + /** flags (see NETIF_FLAG_ above) */ + u16_t flags; + /** descriptive abbreviation */ + char name[2]; + /** number of this interface */ + u8_t num; +#if LWIP_SNMP + /** link type (from "snmp_ifType" enum from snmp.h) */ + u8_t link_type; + /** (estimate) link speed */ + u32_t link_speed; + /** timestamp at last change made (up/down) */ + u32_t ts; + /** counters */ + u32_t ifinoctets; + u32_t ifinucastpkts; + u32_t ifinnucastpkts; + u32_t ifindiscards; + u32_t ifoutoctets; + u32_t ifoutucastpkts; + u32_t ifoutnucastpkts; + u32_t ifoutdiscards; +#endif /* LWIP_SNMP */ +#if LWIP_IGMP + /** This function could be called to add or delete an entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + struct pbuf *loop_first; + struct pbuf *loop_last; +#if LWIP_LOOPBACK_MAX_PBUFS + u16_t loop_cnt_current; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#endif /* ENABLE_LOOPBACK */ +}; + +#if LWIP_SNMP +#define NETIF_INIT_SNMP(netif, type, speed) \ + /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \ + (netif)->link_type = (type); \ + /* your link speed here (units: bits per second) */ \ + (netif)->link_speed = (speed); \ + (netif)->ts = 0; \ + (netif)->ifinoctets = 0; \ + (netif)->ifinucastpkts = 0; \ + (netif)->ifinnucastpkts = 0; \ + (netif)->ifindiscards = 0; \ + (netif)->ifoutoctets = 0; \ + (netif)->ifoutucastpkts = 0; \ + (netif)->ifoutnucastpkts = 0; \ + (netif)->ifoutdiscards = 0 +#else /* LWIP_SNMP */ +#define NETIF_INIT_SNMP(netif, type, speed) +#endif /* LWIP_SNMP */ + + +/** The list of network interfaces. */ +extern struct netif *netif_list; +/** The default network interface. */ +extern struct netif *netif_default; + +void netif_init(void); + +struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input); + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw); +void netif_remove(struct netif * netif); + +/* Returns a network interface given its name. The name is of the form + "et0", where the first two letters are the "name" field in the + netif structure, and the digit is in the num field in the same + structure. */ +struct netif *netif_find(char *name); + +int netif_is_named (struct netif *netif, const char name[3]); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask); +void netif_set_gw(struct netif *netif, ip_addr_t *gw); +void netif_set_pretend_tcp(struct netif *netif, u8_t pretend); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** Ask if an interface is up */ +#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_STATUS_CALLBACK +void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** Ask if a link is up */ +#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0) + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_HOSTNAME +#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0) +#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL) +#endif /* LWIP_NETIF_HOSTNAME */ + +#if LWIP_IGMP +#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0) +#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL) +#endif /* LWIP_IGMP */ + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, ip_addr_t *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)])) +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)]) +#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state)) +s8_t netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr); +void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit); +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETIF_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/netifapi.h b/external/badvpn_dns/lwip/src/include/lwip/netifapi.h new file mode 100644 index 00000000..33318efa --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/netifapi.h @@ -0,0 +1,108 @@ +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef __LWIP_NETIFAPI_H__ +#define __LWIP_NETIFAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*netifapi_void_fn)(struct netif *netif); +typedef err_t (*netifapi_errt_fn)(struct netif *netif); + +struct netifapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + err_t err; + struct netif *netif; + union { + struct { + ip_addr_t *ipaddr; + ip_addr_t *netmask; + ip_addr_t *gw; + void *state; + netif_init_fn init; + netif_input_fn input; + } add; + struct { + netifapi_void_fn voidfunc; + netifapi_errt_fn errtfunc; + } common; + } msg; +}; + +struct netifapi_msg { + void (* function)(struct netifapi_msg_msg *msg); + struct netifapi_msg_msg msg; +}; + + +/* API for application */ +err_t netifapi_netif_add ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw, + void *state, + netif_init_fn init, + netif_input_fn input); + +err_t netifapi_netif_set_addr ( struct netif *netif, + ip_addr_t *ipaddr, + ip_addr_t *netmask, + ip_addr_t *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + netifapi_void_fn voidfunc, + netifapi_errt_fn errtfunc); + +#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL) +#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL) +#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL) +#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL) +#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start) +#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL) +#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start) +#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETIF_API */ + +#endif /* __LWIP_NETIFAPI_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/opt.h b/external/badvpn_dns/lwip/src/include/lwip/opt.h new file mode 100644 index 00000000..e51f8e55 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/opt.h @@ -0,0 +1,2417 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_OPT_H__ +#define __LWIP_OPT_H__ + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ +#include "lwipopts.h" +#include "lwip/debug.h" + +/* + ----------------------------------------------- + ---------- Platform specific locking ---------- + ----------------------------------------------- +*/ + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#ifndef SYS_LIGHTWEIGHT_PROT +#define SYS_LIGHTWEIGHT_PROT 0 +#endif + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#ifndef NO_SYS +#define NO_SYS 0 +#endif + +/** + * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1 + * Mainly for compatibility to old versions. + */ +#ifndef NO_SYS_NO_TIMERS +#define NO_SYS_NO_TIMERS 0 +#endif + +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +#ifndef MEMCPY +#define MEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +#ifndef SMEMCPY +#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +#endif + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#ifndef MEM_LIBC_MALLOC +#define MEM_LIBC_MALLOC 0 +#endif + +/** +* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. +* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution +* speed and usage from interrupts! +*/ +#ifndef MEMP_MEM_MALLOC +#define MEMP_MEM_MALLOC 0 +#endif + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> #define MEM_ALIGNMENT 4 + * 2 byte alignment -> #define MEM_ALIGNMENT 2 + */ +#ifndef MEM_ALIGNMENT +#define MEM_ALIGNMENT 1 +#endif + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#ifndef MEM_SIZE +#define MEM_SIZE 1600 +#endif + +/** + * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array. + * This can be used to individually change the location of each pool. + * Default is one big array for all pools + */ +#ifndef MEMP_SEPARATE_POOLS +#define MEMP_SEPARATE_POOLS 0 +#endif + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#ifndef MEMP_OVERFLOW_CHECK +#define MEMP_OVERFLOW_CHECK 0 +#endif + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#ifndef MEMP_SANITY_CHECK +#define MEMP_SANITY_CHECK 0 +#endif + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#ifndef MEM_USE_POOLS +#define MEM_USE_POOLS 0 +#endif + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 +#endif + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * inlude path somewhere. + */ +#ifndef MEMP_USE_CUSTOM_POOLS +#define MEMP_USE_CUSTOM_POOLS 0 +#endif + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0 +#endif + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#ifndef MEMP_NUM_PBUF +#define MEMP_NUM_PBUF 16 +#endif + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#ifndef MEMP_NUM_RAW_PCB +#define MEMP_NUM_RAW_PCB 4 +#endif + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#ifndef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB 4 +#endif + +/** + * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB 5 +#endif + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_PCB_LISTEN +#define MEMP_NUM_TCP_PCB_LISTEN 8 +#endif + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#ifndef MEMP_NUM_TCP_SEG +#define MEMP_NUM_TCP_SEG 16 +#endif + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#endif + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with IP_FRAG_USES_STATIC_BUF==0 and + * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs + * where the packet is not yet sent when netif->output returns. + */ +#ifndef MEMP_NUM_FRAG_PBUF +#define MEMP_NUM_FRAG_PBUF 15 +#endif + +/** + * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#ifndef MEMP_NUM_ARP_QUEUE +#define MEMP_NUM_ARP_QUEUE 30 +#endif + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members et the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#ifndef MEMP_NUM_IGMP_GROUP +#define MEMP_NUM_IGMP_GROUP 8 +#endif + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts. + * (requires NO_SYS==0) + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) +#endif + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETBUF +#define MEMP_NUM_NETBUF 2 +#endif + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#ifndef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 4 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_API +#define MEMP_NUM_TCPIP_MSG_API 8 +#endif + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#ifndef MEMP_NUM_TCPIP_MSG_INPKT +#define MEMP_NUM_TCPIP_MSG_INPKT 8 +#endif + +/** + * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree. + */ +#ifndef MEMP_NUM_SNMP_NODE +#define MEMP_NUM_SNMP_NODE 50 +#endif + +/** + * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree. + * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least! + */ +#ifndef MEMP_NUM_SNMP_ROOTNODE +#define MEMP_NUM_SNMP_ROOTNODE 30 +#endif + +/** + * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to + * be changed normally) - 2 of these are used per request (1 for input, + * 1 for output) + */ +#ifndef MEMP_NUM_SNMP_VARBIND +#define MEMP_NUM_SNMP_VARBIND 2 +#endif + +/** + * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used + * (does not have to be changed normally) - 3 of these are used per request + * (1 for the value read and 2 for OIDs - input and output) + */ +#ifndef MEMP_NUM_SNMP_VALUE +#define MEMP_NUM_SNMP_VALUE 3 +#endif + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#ifndef MEMP_NUM_NETDB +#define MEMP_NUM_NETDB 1 +#endif + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#ifndef MEMP_NUM_LOCALHOSTLIST +#define MEMP_NUM_LOCALHOSTLIST 1 +#endif + +/** + * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE + * interfaces (only used with PPPOE_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOE_INTERFACES +#define MEMP_NUM_PPPOE_INTERFACES 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#ifndef LWIP_ARP +#define LWIP_ARP 1 +#endif + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#ifndef ARP_TABLE_SIZE +#define ARP_TABLE_SIZE 10 +#endif + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#ifndef ARP_QUEUEING +#define ARP_QUEUEING 0 +#endif + +/** + * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be + * updated with the source MAC and IP addresses supplied in the packet. + * You may want to disable this if you do not trust LAN peers to have the + * correct addresses, or as a limited approach to attempt to handle + * spoofing. If disabled, lwIP will need to make a new ARP request if + * the peer is not already in the ARP table, adding a little latency. + * The peer *is* in the ARP table if it requested our address before. + * Also notice that this slows down input processing of every IP packet! + */ +#ifndef ETHARP_TRUST_IP_MAC +#define ETHARP_TRUST_IP_MAC 0 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#ifndef ETHARP_SUPPORT_VLAN +#define ETHARP_SUPPORT_VLAN 0 +#endif + +/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP + * might be disabled + */ +#ifndef LWIP_ETHERNET +#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT) +#endif + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#ifndef ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#endif + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#ifndef ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 +#endif + + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#ifndef IP_FORWARD +#define IP_FORWARD 0 +#endif + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#ifndef IP_OPTIONS_ALLOWED +#define IP_OPTIONS_ALLOWED 1 +#endif + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#ifndef IP_REASSEMBLY +#define IP_REASSEMBLY 1 +#endif + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#ifndef IP_FRAG +#define IP_FRAG 1 +#endif + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#ifndef IP_REASS_MAXAGE +#define IP_REASS_MAXAGE 3 +#endif + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#ifndef IP_REASS_MAX_PBUFS +#define IP_REASS_MAX_PBUFS 10 +#endif + +/** + * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP + * fragmentation. Otherwise pbufs are allocated and reference the original + * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1, + * new PBUF_RAM pbufs are used for fragments). + * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs! + */ +#ifndef IP_FRAG_USES_STATIC_BUF +#define IP_FRAG_USES_STATIC_BUF 0 +#endif + +/** + * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer + * (requires IP_FRAG_USES_STATIC_BUF==1) + */ +#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU) +#define IP_FRAG_MAX_MTU 1500 +#endif + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#ifndef IP_DEFAULT_TTL +#define IP_DEFAULT_TTL 255 +#endif + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#ifndef IP_SOF_BROADCAST +#define IP_SOF_BROADCAST 0 +#endif + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#ifndef IP_SOF_BROADCAST_RECV +#define IP_SOF_BROADCAST_RECV 0 +#endif + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +#endif + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#ifndef LWIP_ICMP +#define LWIP_ICMP 1 +#endif + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#ifndef ICMP_TTL +#define ICMP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#ifndef LWIP_BROADCAST_PING +#define LWIP_BROADCAST_PING 0 +#endif + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#ifndef LWIP_MULTICAST_PING +#define LWIP_MULTICAST_PING 0 +#endif + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef LWIP_RAW +#define LWIP_RAW 1 +#endif + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#ifndef RAW_TTL +#define RAW_TTL (IP_DEFAULT_TTL) +#endif + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#ifndef LWIP_DHCP +#define LWIP_DHCP 0 +#endif + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#ifndef DHCP_DOES_ARP_CHECK +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP +#define LWIP_DHCP_AUTOIP_COOP 0 +#endif + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP. This can be set + * as low as 1 to get an AutoIP address very quickly, but you should + * be prepared to handle a changing IP address when DHCP overrides + * AutoIP. + */ +#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +#endif + +/* + ---------------------------------- + ---------- SNMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP + * transport. + */ +#ifndef LWIP_SNMP +#define LWIP_SNMP 0 +#endif + +/** + * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will + * allow. At least one request buffer is required. + * Does not have to be changed unless external MIBs answer request asynchronously + */ +#ifndef SNMP_CONCURRENT_REQUESTS +#define SNMP_CONCURRENT_REQUESTS 1 +#endif + +/** + * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap + * destination is required + */ +#ifndef SNMP_TRAP_DESTINATIONS +#define SNMP_TRAP_DESTINATIONS 1 +#endif + +/** + * SNMP_PRIVATE_MIB: + * When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. + */ +#ifndef SNMP_PRIVATE_MIB +#define SNMP_PRIVATE_MIB 0 +#endif + +/** + * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not + * a safe action and disabled when SNMP_SAFE_REQUESTS = 1). + * Unsafe requests are disabled by default! + */ +#ifndef SNMP_SAFE_REQUESTS +#define SNMP_SAFE_REQUESTS 1 +#endif + +/** + * The maximum length of strings used. This affects the size of + * MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_OCTET_STRING_LEN +#define SNMP_MAX_OCTET_STRING_LEN 127 +#endif + +/** + * The maximum depth of the SNMP tree. + * With private MIBs enabled, this depends on your MIB! + * This affects the size of MEMP_SNMP_VALUE elements. + */ +#ifndef SNMP_MAX_TREE_DEPTH +#define SNMP_MAX_TREE_DEPTH 15 +#endif + +/** + * The size of the MEMP_SNMP_VALUE elements, normally calculated from + * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH. + */ +#ifndef SNMP_MAX_VALUE_SIZE +#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH)) +#endif + +/* + ---------------------------------- + ---------- IGMP options ---------- + ---------------------------------- +*/ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#ifndef LWIP_IGMP +#define LWIP_IGMP 0 +#endif + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#ifndef LWIP_DNS +#define LWIP_DNS 0 +#endif + +/** DNS maximum number of entries to maintain locally. */ +#ifndef DNS_TABLE_SIZE +#define DNS_TABLE_SIZE 4 +#endif + +/** DNS maximum host name length supported in the name table. */ +#ifndef DNS_MAX_NAME_LENGTH +#define DNS_MAX_NAME_LENGTH 256 +#endif + +/** The maximum of DNS servers */ +#ifndef DNS_MAX_SERVERS +#define DNS_MAX_SERVERS 2 +#endif + +/** DNS do a name checking between the query and the response. */ +#ifndef DNS_DOES_NAME_CHECK +#define DNS_DOES_NAME_CHECK 1 +#endif + +/** DNS message max. size. Default value is RFC compliant. */ +#ifndef DNS_MSG_SIZE +#define DNS_MSG_SIZE 512 +#endif + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, + * you have to define + * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}} + * (an array of structs name/address, where address is an u32_t in network + * byte order). + * + * Instead, you can also use an external function: + * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name) + * that returns the IP address or INADDR_NONE if not found. + */ +#ifndef DNS_LOCAL_HOSTLIST +#define DNS_LOCAL_HOSTLIST 0 +#endif /* DNS_LOCAL_HOSTLIST */ + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#ifndef LWIP_UDP +#define LWIP_UDP 1 +#endif + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#ifndef LWIP_UDPLITE +#define LWIP_UDPLITE 0 +#endif + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#ifndef UDP_TTL +#define UDP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#ifndef LWIP_NETBUF_RECVINFO +#define LWIP_NETBUF_RECVINFO 0 +#endif + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#ifndef LWIP_TCP +#define LWIP_TCP 1 +#endif + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#ifndef TCP_TTL +#define TCP_TTL (IP_DEFAULT_TTL) +#endif + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well + */ +#ifndef TCP_WND +#define TCP_WND (4 * TCP_MSS) +#endif + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#ifndef TCP_MAXRTX +#define TCP_MAXRTX 12 +#endif + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#ifndef TCP_SYNMAXRTX +#define TCP_SYNMAXRTX 6 +#endif + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#ifndef TCP_QUEUE_OOSEQ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) +#endif + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#ifndef TCP_MSS +#define TCP_MSS 536 +#endif + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#ifndef TCP_CALCULATE_EFF_SEND_MSS +#define TCP_CALCULATE_EFF_SEND_MSS 1 +#endif + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#ifndef TCP_SND_BUF +#define TCP_SND_BUF (2 * TCP_MSS) +#endif + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#ifndef TCP_SND_QUEUELEN +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#ifndef TCP_SNDQUEUELOWAT +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) +#endif + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 +#endif + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#ifndef TCP_LISTEN_BACKLOG +#define TCP_LISTEN_BACKLOG 0 +#endif + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#ifndef TCP_DEFAULT_LISTEN_BACKLOG +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff +#endif + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#ifndef TCP_OVERSIZE +#define TCP_OVERSIZE TCP_MSS +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + */ +#ifndef LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_TIMESTAMPS 0 +#endif + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#ifndef TCP_WND_UPDATE_THRESHOLD +#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4) +#endif + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#endif + + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#ifndef PBUF_LINK_HLEN +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accomodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#ifndef PBUF_POOL_BUFSIZE +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN) +#endif + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#ifndef LWIP_NETIF_HOSTNAME +#define LWIP_NETIF_HOSTNAME 0 +#endif + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#ifndef LWIP_NETIF_API +#define LWIP_NETIF_API 0 +#endif + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquistion) + */ +#ifndef LWIP_NETIF_STATUS_CALLBACK +#define LWIP_NETIF_STATUS_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#ifndef LWIP_NETIF_LINK_CALLBACK +#define LWIP_NETIF_LINK_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_CALLBACK 0 +#endif + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#ifndef LWIP_NETIF_HWADDRHINT +#define LWIP_NETIF_HWADDRHINT 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#ifndef LWIP_NETIF_LOOPBACK +#define LWIP_NETIF_LOOPBACK 0 +#endif + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#ifndef LWIP_LOOPBACK_MAX_PBUFS +#define LWIP_LOOPBACK_MAX_PBUFS 0 +#endif + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +#endif + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#ifndef LWIP_NETIF_TX_SINGLE_PBUF +#define LWIP_NETIF_TX_SINGLE_PBUF 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c + */ +#ifndef LWIP_HAVE_LOOPIF +#define LWIP_HAVE_LOOPIF 0 +#endif + +/* + ------------------------------------ + ---------- SLIPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c + */ +#ifndef LWIP_HAVE_SLIPIF +#define LWIP_HAVE_SLIPIF 0 +#endif + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#ifndef TCPIP_THREAD_NAME +#define TCPIP_THREAD_NAME "tcpip_thread" +#endif + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_STACKSIZE +#define TCPIP_THREAD_STACKSIZE 0 +#endif + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef TCPIP_THREAD_PRIO +#define TCPIP_THREAD_PRIO 1 +#endif + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#ifndef TCPIP_MBOX_SIZE +#define TCPIP_MBOX_SIZE 0 +#endif + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#ifndef SLIPIF_THREAD_NAME +#define SLIPIF_THREAD_NAME "slipif_loop" +#endif + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_STACKSIZE +#define SLIPIF_THREAD_STACKSIZE 0 +#endif + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef SLIPIF_THREAD_PRIO +#define SLIPIF_THREAD_PRIO 1 +#endif + +/** + * PPP_THREAD_NAME: The name assigned to the pppInputThread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppInputThread" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_STACKSIZE +#define PPP_THREAD_STACKSIZE 0 +#endif + +/** + * PPP_THREAD_PRIO: The priority assigned to the pppInputThread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef PPP_THREAD_PRIO +#define PPP_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#ifndef DEFAULT_THREAD_NAME +#define DEFAULT_THREAD_NAME "lwIP" +#endif + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_STACKSIZE +#define DEFAULT_THREAD_STACKSIZE 0 +#endif + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#ifndef DEFAULT_THREAD_PRIO +#define DEFAULT_THREAD_PRIO 1 +#endif + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_RAW_RECVMBOX_SIZE +#define DEFAULT_RAW_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_UDP_RECVMBOX_SIZE +#define DEFAULT_UDP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#ifndef DEFAULT_TCP_RECVMBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE 0 +#endif + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#ifndef DEFAULT_ACCEPTMBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE 0 +#endif + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING +#define LWIP_TCPIP_CORE_LOCKING 0 +#endif + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!) + * Don't use it if you're not an active lwIP project member + */ +#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 +#endif + +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names. + * (only used if you use sockets.c) + */ +#ifndef LWIP_COMPAT_SOCKETS +#define LWIP_COMPAT_SOCKETS 1 +#endif + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#ifndef LWIP_POSIX_SOCKETS_IO_NAMES +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 +#endif + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#ifndef LWIP_TCP_KEEPALIVE +#define LWIP_TCP_KEEPALIVE 0 +#endif + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 0 +#endif + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#ifndef LWIP_SO_RCVBUF +#define LWIP_SO_RCVBUF 0 +#endif + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#ifndef RECV_BUFSIZE_DEFAULT +#define RECV_BUFSIZE_DEFAULT INT_MAX +#endif + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#ifndef SO_REUSE +#define SO_REUSE 0 +#endif + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#ifndef SO_REUSE_RXTOALL +#define SO_REUSE_RXTOALL 0 +#endif + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#ifndef LWIP_FIONREAD_LINUXMODE +#define LWIP_FIONREAD_LINUXMODE 0 +#endif + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#ifndef LWIP_STATS +#define LWIP_STATS 1 +#endif + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#ifndef LWIP_STATS_DISPLAY +#define LWIP_STATS_DISPLAY 0 +#endif + +/** + * LINK_STATS==1: Enable link stats. + */ +#ifndef LINK_STATS +#define LINK_STATS 1 +#endif + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#ifndef ETHARP_STATS +#define ETHARP_STATS (LWIP_ARP) +#endif + +/** + * IP_STATS==1: Enable IP stats. + */ +#ifndef IP_STATS +#define IP_STATS 1 +#endif + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#ifndef IPFRAG_STATS +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) +#endif + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#ifndef ICMP_STATS +#define ICMP_STATS 1 +#endif + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#ifndef IGMP_STATS +#define IGMP_STATS (LWIP_IGMP) +#endif + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#ifndef UDP_STATS +#define UDP_STATS (LWIP_UDP) +#endif + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#ifndef TCP_STATS +#define TCP_STATS (LWIP_TCP) +#endif + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#ifndef MEM_STATS +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) +#endif + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#ifndef MEMP_STATS +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) +#endif + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#ifndef SYS_STATS +#define SYS_STATS (NO_SYS == 0) +#endif + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#ifndef IP6_STATS +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#ifndef ICMP6_STATS +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#ifndef IP6_FRAG_STATS +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#ifndef MLD6_STATS +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#ifndef ND6_STATS +#define ND6_STATS (LWIP_IPV6) +#endif + +#else + +#define LINK_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 + +#endif /* LWIP_STATS */ + +/* + --------------------------------- + ---------- PPP options ---------- + --------------------------------- +*/ +/** + * PPP_SUPPORT==1: Enable PPP. + */ +#ifndef PPP_SUPPORT +#define PPP_SUPPORT 0 +#endif + +/** + * PPPOE_SUPPORT==1: Enable PPP Over Ethernet + */ +#ifndef PPPOE_SUPPORT +#define PPPOE_SUPPORT 0 +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +#if PPP_SUPPORT + +/** + * NUM_PPP: Max PPP sessions. + */ +#ifndef NUM_PPP +#define NUM_PPP 1 +#endif + +/** + * PAP_SUPPORT==1: Support PAP. + */ +#ifndef PAP_SUPPORT +#define PAP_SUPPORT 0 +#endif + +/** + * CHAP_SUPPORT==1: Support CHAP. + */ +#ifndef CHAP_SUPPORT +#define CHAP_SUPPORT 0 +#endif + +/** + * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif + +/** + * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CBCP_SUPPORT +#define CBCP_SUPPORT 0 +#endif + +/** + * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef CCP_SUPPORT +#define CCP_SUPPORT 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 0 +#endif + +/** + * MD5_SUPPORT==1: Support MD5 (see also CHAP). + */ +#ifndef MD5_SUPPORT +#define MD5_SUPPORT 0 +#endif + +/* + * Timeouts + */ +#ifndef FSM_DEFTIMEOUT +#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef FSM_DEFMAXTERMREQS +#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXCONFREQS +#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#endif + +#ifndef FSM_DEFMAXNAKLOOPS +#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif + +#ifndef UPAP_DEFTIMEOUT +#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +/* Interval in seconds between keepalive echo requests, 0 to disable. */ +#ifndef LCP_ECHOINTERVAL +#define LCP_ECHOINTERVAL 0 +#endif + +/* Number of unanswered echo requests before failure. */ +#ifndef LCP_MAXECHOFAILS +#define LCP_MAXECHOFAILS 3 +#endif + +/* Max Xmit idle time (in jiffies) before resend flag char. */ +#ifndef PPP_MAXIDLEFLAG +#define PPP_MAXIDLEFLAG 100 +#endif + +/* + * Packet sizes + * + * Note - lcp shouldn't be allowed to negotiate stuff outside these + * limits. See lcp.h in the pppd directory. + * (XXX - these constants should simply be shared by lcp.c instead + * of living in lcp.h) + */ +#define PPP_MTU 1500 /* Default MTU (size of Info field) */ +#ifndef PPP_MAXMTU +/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */ +#define PPP_MAXMTU 1500 /* Largest MTU we allow */ +#endif +#define PPP_MINMTU 64 +#define PPP_MRU 1500 /* default MRU = max length of info field */ +#define PPP_MAXMRU 1500 /* Largest MRU we allow */ +#ifndef PPP_DEFMRU +#define PPP_DEFMRU 296 /* Try for this */ +#endif +#define PPP_MINMRU 128 /* No MRUs below this */ + +#ifndef MAXNAMELEN +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#endif +#ifndef MAXSECRETLEN +#define MAXSECRETLEN 256 /* max length of password or secret */ +#endif + +#endif /* PPP_SUPPORT */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#ifndef CHECKSUM_GEN_IP +#define CHECKSUM_GEN_IP 1 +#endif + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#ifndef CHECKSUM_GEN_UDP +#define CHECKSUM_GEN_UDP 1 +#endif + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#ifndef CHECKSUM_GEN_TCP +#define CHECKSUM_GEN_TCP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 1 +#endif + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#ifndef CHECKSUM_CHECK_IP +#define CHECKSUM_CHECK_IP 1 +#endif + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#ifndef CHECKSUM_CHECK_UDP +#define CHECKSUM_CHECK_UDP 1 +#endif + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#ifndef CHECKSUM_CHECK_TCP +#define CHECKSUM_CHECK_TCP 1 +#endif + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#ifndef LWIP_CHECKSUM_ON_COPY +#define LWIP_CHECKSUM_ON_COPY 0 +#endif + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#ifndef LWIP_IPV6_NUM_ADDRESSES +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#ifndef LWIP_IPV6_FORWARD +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#ifndef LWIP_ICMP6 +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#ifndef LWIP_ICMP6_HL +#define LWIP_ICMP6_HL 255 +#endif + +/** + * LWIP_ICMP6_CHECKSUM_CHECK==1: verify checksum on ICMPv6 packets + */ +#ifndef LWIP_ICMP6_CHECKSUM_CHECK +#define LWIP_ICMP6_CHECKSUM_CHECK 1 +#endif + +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + */ +#ifndef LWIP_IPV6_MLD +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined. + */ +#ifndef MEMP_NUM_MLD6_GROUP +#define MEMP_NUM_MLD6_GROUP 4 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#ifndef LWIP_IPV6_FRAG +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#ifndef LWIP_IPV6_REASS +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#ifndef LWIP_ND6_QUEUEING +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#ifndef MEMP_NUM_ND6_QUEUE +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#ifndef LWIP_ND6_NUM_NEIGHBORS +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#ifndef LWIP_ND6_NUM_DESTINATIONS +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#ifndef LWIP_ND6_NUM_PREFIXES +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#ifndef LWIP_ND6_NUM_ROUTERS +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#ifndef LWIP_ND6_REACHABLE_TIME +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#ifndef LWIP_ND6_RETRANS_TIMER +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#ifndef LWIP_ND6_ALLOW_RA_UPDATES +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#ifndef LWIP_IPV6_AUTOCONFIG +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts. + */ +#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#ifndef LWIP_IPV6_DHCP6 +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + */ +#ifndef LWIP_DBG_MIN_LEVEL +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +#endif + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + */ +#ifndef LWIP_DBG_TYPES_ON +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON +#endif + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#ifndef ETHARP_DEBUG +#define ETHARP_DEBUG LWIP_DBG_OFF +#endif + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#ifndef NETIF_DEBUG +#define NETIF_DEBUG LWIP_DBG_OFF +#endif + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#ifndef PBUF_DEBUG +#define PBUF_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#ifndef API_LIB_DEBUG +#define API_LIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#ifndef API_MSG_DEBUG +#define API_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#ifndef SOCKETS_DEBUG +#define SOCKETS_DEBUG LWIP_DBG_OFF +#endif + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#ifndef ICMP_DEBUG +#define ICMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#ifndef IGMP_DEBUG +#define IGMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#ifndef INET_DEBUG +#define INET_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#ifndef IP_DEBUG +#define IP_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#ifndef IP_REASS_DEBUG +#define IP_REASS_DEBUG LWIP_DBG_OFF +#endif + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#ifndef RAW_DEBUG +#define RAW_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#ifndef MEM_DEBUG +#define MEM_DEBUG LWIP_DBG_OFF +#endif + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#ifndef MEMP_DEBUG +#define MEMP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#ifndef SYS_DEBUG +#define SYS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#ifndef TIMERS_DEBUG +#define TIMERS_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#ifndef TCP_DEBUG +#define TCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#ifndef TCP_INPUT_DEBUG +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#ifndef TCP_FR_DEBUG +#define TCP_FR_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#ifndef TCP_RTO_DEBUG +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#ifndef TCP_CWND_DEBUG +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#ifndef TCP_WND_DEBUG +#define TCP_WND_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#ifndef TCP_OUTPUT_DEBUG +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#ifndef TCP_RST_DEBUG +#define TCP_RST_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#ifndef TCP_QLEN_DEBUG +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#endif + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#ifndef UDP_DEBUG +#define UDP_DEBUG LWIP_DBG_OFF +#endif + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#ifndef TCPIP_DEBUG +#define TCPIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * PPP_DEBUG: Enable debugging for PPP. + */ +#ifndef PPP_DEBUG +#define PPP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#ifndef SLIP_DEBUG +#define SLIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#ifndef DHCP_DEBUG +#define DHCP_DEBUG LWIP_DBG_OFF +#endif + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#ifndef AUTOIP_DEBUG +#define AUTOIP_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MSG_DEBUG: Enable debugging for SNMP messages. + */ +#ifndef SNMP_MSG_DEBUG +#define SNMP_MSG_DEBUG LWIP_DBG_OFF +#endif + +/** + * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs. + */ +#ifndef SNMP_MIB_DEBUG +#define SNMP_MIB_DEBUG LWIP_DBG_OFF +#endif + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#ifndef DNS_DEBUG +#define DNS_DEBUG LWIP_DBG_OFF +#endif + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_OFF +#endif + +#endif /* __LWIP_OPT_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/pbuf.h b/external/badvpn_dns/lwip/src/include/lwip/pbuf.h new file mode 100644 index 00000000..4f8dca8a --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/pbuf.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __LWIP_PBUF_H__ +#define __LWIP_PBUF_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Currently, the pbuf_custom code is only needed for one specific configuration + * of IP_FRAG */ +#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) + +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + +#define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else +#define PBUF_IP_HLEN 20 +#endif + +typedef enum { + PBUF_TRANSPORT, + PBUF_IP, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_RAM, /* pbuf data is stored in RAM */ + PBUF_ROM, /* pbuf data is stored in ROM */ + PBUF_REF, /* pbuf comes from the pbuf pool */ + PBUF_POOL /* pbuf payload refers to RAM */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U +/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a + a pbuf differently */ +#define PBUF_FLAG_IS_CUSTOM 0x02U +/** indicates this pbuf is UDP multicast to be looped back */ +#define PBUF_FLAG_MCASTLOOP 0x04U +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U + +struct pbuf { + /** next pbuf in singly linked pbuf chain */ + struct pbuf *next; + + /** pointer to the actual data in the buffer */ + void *payload; + + /** + * total length of this buffer and all next buffers in chain + * belonging to the same packet. + * + * For non-queue packet chains this is the invariant: + * p->tot_len == p->len + (p->next? p->next->tot_len: 0) + */ + u16_t tot_len; + + /** length of this buffer */ + u16_t len; + + /** pbuf_type as u8_t instead of enum to save space */ + u8_t /*pbuf_type*/ type; + + /** misc flags */ + u8_t flags; + + /** + * the reference count always equals the number of pointers + * that refer to this pbuf. This can be pointers from an application, + * the stack itself, or pbuf->next pointers from a chain. + */ + u16_t ref; +}; + +#if LWIP_SUPPORT_CUSTOM_PBUF +/** Prototype for a function to free a custom pbuf */ +typedef void (*pbuf_free_custom_fn)(struct pbuf *p); + +/** A custom pbuf: like a pbuf, but following a function pointer to free it. */ +struct pbuf_custom { + /** The actual pbuf */ + struct pbuf pbuf; + /** This function is called when pbuf_free deallocates this pbuf(_custom) */ + pbuf_free_custom_fn custom_free_function; +}; +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ + +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */ +#ifndef PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_FREE_OOSEQ 1 +#endif /* PBUF_POOL_FREE_OOSEQ */ +#if NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(void); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + +/* Initializes the pbuf module. This call is empty for now, but may not be in future. */ +#define pbuf_init() + +struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type); +#if LWIP_SUPPORT_CUSTOM_PBUF +struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, + struct pbuf_custom *p, void *payload_mem, + u16_t payload_mem_len); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#if LWIP_CHECKSUM_ON_COPY +err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, + u16_t len, u16_t *chksum); +#endif /* LWIP_CHECKSUM_ON_COPY */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset); +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/raw.h b/external/badvpn_dns/lwip/src/include/lwip/raw.h new file mode 100644 index 00000000..f0c8ed47 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/raw.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_RAW_H__ +#define __LWIP_RAW_H__ + +#include "lwip/opt.h" + +#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb; + +/** Function prototype for raw pcb receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip_addr_t *addr); + +#if LWIP_IPV6 +/** Function prototype for raw pcb IPv6 receive callback functions. + * @param arg user supplied argument (raw_pcb.recv_arg) + * @param pcb the raw_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @return 1 if the packet was 'eaten' (aka. deleted), + * 0 if the packet lives on + * If returning 1, the callback is responsible for freeing the pbuf + * if it's not used any more. + */ +typedef u8_t (*raw_recv_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6; +#else +#define RAW_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + union { + raw_recv_fn ip4; + RAW_PCB_RECV_IP6 + } recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +#if LWIP_IPV6 +struct raw_pcb * raw_new_ip6 (u8_t proto); +#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr)) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr)) +#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg) +#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr)) +#endif /* LWIP_IPV6 */ + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/sio.h b/external/badvpn_dns/lwip/src/include/lwip/sio.h new file mode 100644 index 00000000..28ae2f22 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/sio.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + */ + +/* + * This is the interface to the platform specific serial IO module + * It needs to be implemented by those platforms which need SLIP or PPP + */ + +#ifndef __SIO_H__ +#define __SIO_H__ + +#include "lwip/arch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If you want to define sio_fd_t elsewhere or differently, + define this in your cc.h file. */ +#ifndef __sio_fd_t_defined +typedef void * sio_fd_t; +#endif + +/* The following functions can be defined to something else in your cc.h file + or be implemented in your custom sio.c file. */ + +#ifndef sio_open +/** + * Opens a serial device for communication. + * + * @param devnum device number + * @return handle to serial device if successful, NULL otherwise + */ +sio_fd_t sio_open(u8_t devnum); +#endif + +#ifndef sio_send +/** + * Sends a single character to the serial device. + * + * @param c character to send + * @param fd serial device handle + * + * @note This function will block until the character can be sent. + */ +void sio_send(u8_t c, sio_fd_t fd); +#endif + +#ifndef sio_recv +/** + * Receives a single character from the serial device. + * + * @param fd serial device handle + * + * @note This function will block until a character is received. + */ +u8_t sio_recv(sio_fd_t fd); +#endif + +#ifndef sio_read +/** + * Reads from the serial device. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received - may be 0 if aborted by sio_read_abort + * + * @note This function will block until data can be received. The blocking + * can be cancelled by calling sio_read_abort(). + */ +u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_tryread +/** + * Tries to read from the serial device. Same as sio_read but returns + * immediately if no data is available and never blocks. + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @return number of bytes actually received + */ +u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_write +/** + * Writes to the serial device. + * + * @param fd serial device handle + * @param data pointer to data to send + * @param len length (in bytes) of data to send + * @return number of bytes actually sent + * + * @note This function will block until all data can be sent. + */ +u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/snmp.h b/external/badvpn_dns/lwip/src/include/lwip/snmp.h new file mode 100644 index 00000000..2ed043dd --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/snmp.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2001, 2002 Leon Woestenberg + * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Leon Woestenberg + * + */ +#ifndef __LWIP_SNMP_H__ +#define __LWIP_SNMP_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/ip_addr.h" + +struct udp_pcb; +struct netif; + +/** + * @see RFC1213, "MIB-II, 6. Definitions" + */ +enum snmp_ifType { + snmp_ifType_other=1, /* none of the following */ + snmp_ifType_regular1822, + snmp_ifType_hdh1822, + snmp_ifType_ddn_x25, + snmp_ifType_rfc877_x25, + snmp_ifType_ethernet_csmacd, + snmp_ifType_iso88023_csmacd, + snmp_ifType_iso88024_tokenBus, + snmp_ifType_iso88025_tokenRing, + snmp_ifType_iso88026_man, + snmp_ifType_starLan, + snmp_ifType_proteon_10Mbit, + snmp_ifType_proteon_80Mbit, + snmp_ifType_hyperchannel, + snmp_ifType_fddi, + snmp_ifType_lapb, + snmp_ifType_sdlc, + snmp_ifType_ds1, /* T-1 */ + snmp_ifType_e1, /* european equiv. of T-1 */ + snmp_ifType_basicISDN, + snmp_ifType_primaryISDN, /* proprietary serial */ + snmp_ifType_propPointToPointSerial, + snmp_ifType_ppp, + snmp_ifType_softwareLoopback, + snmp_ifType_eon, /* CLNP over IP [11] */ + snmp_ifType_ethernet_3Mbit, + snmp_ifType_nsip, /* XNS over IP */ + snmp_ifType_slip, /* generic SLIP */ + snmp_ifType_ultra, /* ULTRA technologies */ + snmp_ifType_ds3, /* T-3 */ + snmp_ifType_sip, /* SMDS */ + snmp_ifType_frame_relay +}; + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +/** SNMP "sysuptime" Interval */ +#define SNMP_SYSUPTIME_INTERVAL 10 + +/** fixed maximum length for object identifier type */ +#define LWIP_SNMP_OBJ_ID_LEN 32 + +/** internal object identifier representation */ +struct snmp_obj_id +{ + u8_t len; + s32_t id[LWIP_SNMP_OBJ_ID_LEN]; +}; + +/* system */ +void snmp_set_sysdesr(u8_t* str, u8_t* len); +void snmp_set_sysobjid(struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid); +void snmp_inc_sysuptime(void); +void snmp_add_sysuptime(u32_t value); +void snmp_get_sysuptime(u32_t *value); +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen); +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen); + +/* network interface */ +void snmp_add_ifinoctets(struct netif *ni, u32_t value); +void snmp_inc_ifinucastpkts(struct netif *ni); +void snmp_inc_ifinnucastpkts(struct netif *ni); +void snmp_inc_ifindiscards(struct netif *ni); +void snmp_add_ifoutoctets(struct netif *ni, u32_t value); +void snmp_inc_ifoutucastpkts(struct netif *ni); +void snmp_inc_ifoutnucastpkts(struct netif *ni); +void snmp_inc_ifoutdiscards(struct netif *ni); +void snmp_inc_iflist(void); +void snmp_dec_iflist(void); + +/* ARP (for atTable and ipNetToMediaTable) */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip); +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip); + +/* IP */ +void snmp_inc_ipinreceives(void); +void snmp_inc_ipinhdrerrors(void); +void snmp_inc_ipinaddrerrors(void); +void snmp_inc_ipforwdatagrams(void); +void snmp_inc_ipinunknownprotos(void); +void snmp_inc_ipindiscards(void); +void snmp_inc_ipindelivers(void); +void snmp_inc_ipoutrequests(void); +void snmp_inc_ipoutdiscards(void); +void snmp_inc_ipoutnoroutes(void); +void snmp_inc_ipreasmreqds(void); +void snmp_inc_ipreasmoks(void); +void snmp_inc_ipreasmfails(void); +void snmp_inc_ipfragoks(void); +void snmp_inc_ipfragfails(void); +void snmp_inc_ipfragcreates(void); +void snmp_inc_iproutingdiscards(void); +void snmp_insert_ipaddridx_tree(struct netif *ni); +void snmp_delete_ipaddridx_tree(struct netif *ni); +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni); +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni); + +/* ICMP */ +void snmp_inc_icmpinmsgs(void); +void snmp_inc_icmpinerrors(void); +void snmp_inc_icmpindestunreachs(void); +void snmp_inc_icmpintimeexcds(void); +void snmp_inc_icmpinparmprobs(void); +void snmp_inc_icmpinsrcquenchs(void); +void snmp_inc_icmpinredirects(void); +void snmp_inc_icmpinechos(void); +void snmp_inc_icmpinechoreps(void); +void snmp_inc_icmpintimestamps(void); +void snmp_inc_icmpintimestampreps(void); +void snmp_inc_icmpinaddrmasks(void); +void snmp_inc_icmpinaddrmaskreps(void); +void snmp_inc_icmpoutmsgs(void); +void snmp_inc_icmpouterrors(void); +void snmp_inc_icmpoutdestunreachs(void); +void snmp_inc_icmpouttimeexcds(void); +void snmp_inc_icmpoutparmprobs(void); +void snmp_inc_icmpoutsrcquenchs(void); +void snmp_inc_icmpoutredirects(void); +void snmp_inc_icmpoutechos(void); +void snmp_inc_icmpoutechoreps(void); +void snmp_inc_icmpouttimestamps(void); +void snmp_inc_icmpouttimestampreps(void); +void snmp_inc_icmpoutaddrmasks(void); +void snmp_inc_icmpoutaddrmaskreps(void); + +/* TCP */ +void snmp_inc_tcpactiveopens(void); +void snmp_inc_tcppassiveopens(void); +void snmp_inc_tcpattemptfails(void); +void snmp_inc_tcpestabresets(void); +void snmp_inc_tcpinsegs(void); +void snmp_inc_tcpoutsegs(void); +void snmp_inc_tcpretranssegs(void); +void snmp_inc_tcpinerrs(void); +void snmp_inc_tcpoutrsts(void); + +/* UDP */ +void snmp_inc_udpindatagrams(void); +void snmp_inc_udpnoports(void); +void snmp_inc_udpinerrors(void); +void snmp_inc_udpoutdatagrams(void); +void snmp_insert_udpidx_tree(struct udp_pcb *pcb); +void snmp_delete_udpidx_tree(struct udp_pcb *pcb); + +/* SNMP */ +void snmp_inc_snmpinpkts(void); +void snmp_inc_snmpoutpkts(void); +void snmp_inc_snmpinbadversions(void); +void snmp_inc_snmpinbadcommunitynames(void); +void snmp_inc_snmpinbadcommunityuses(void); +void snmp_inc_snmpinasnparseerrs(void); +void snmp_inc_snmpintoobigs(void); +void snmp_inc_snmpinnosuchnames(void); +void snmp_inc_snmpinbadvalues(void); +void snmp_inc_snmpinreadonlys(void); +void snmp_inc_snmpingenerrs(void); +void snmp_add_snmpintotalreqvars(u8_t value); +void snmp_add_snmpintotalsetvars(u8_t value); +void snmp_inc_snmpingetrequests(void); +void snmp_inc_snmpingetnexts(void); +void snmp_inc_snmpinsetrequests(void); +void snmp_inc_snmpingetresponses(void); +void snmp_inc_snmpintraps(void); +void snmp_inc_snmpouttoobigs(void); +void snmp_inc_snmpoutnosuchnames(void); +void snmp_inc_snmpoutbadvalues(void); +void snmp_inc_snmpoutgenerrs(void); +void snmp_inc_snmpoutgetrequests(void); +void snmp_inc_snmpoutgetnexts(void); +void snmp_inc_snmpoutsetrequests(void); +void snmp_inc_snmpoutgetresponses(void); +void snmp_inc_snmpouttraps(void); +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid); +void snmp_set_snmpenableauthentraps(u8_t *value); +void snmp_get_snmpenableauthentraps(u8_t *value); + +/* LWIP_SNMP support not available */ +/* define everything to be empty */ +#else + +/* system */ +#define snmp_set_sysdesr(str, len) +#define snmp_set_sysobjid(oid); +#define snmp_get_sysobjid_ptr(oid) +#define snmp_inc_sysuptime() +#define snmp_add_sysuptime(value) +#define snmp_get_sysuptime(value) +#define snmp_set_syscontact(ocstr, ocstrlen); +#define snmp_set_sysname(ocstr, ocstrlen); +#define snmp_set_syslocation(ocstr, ocstrlen); + +/* network interface */ +#define snmp_add_ifinoctets(ni,value) +#define snmp_inc_ifinucastpkts(ni) +#define snmp_inc_ifinnucastpkts(ni) +#define snmp_inc_ifindiscards(ni) +#define snmp_add_ifoutoctets(ni,value) +#define snmp_inc_ifoutucastpkts(ni) +#define snmp_inc_ifoutnucastpkts(ni) +#define snmp_inc_ifoutdiscards(ni) +#define snmp_inc_iflist() +#define snmp_dec_iflist() + +/* ARP */ +#define snmp_insert_arpidx_tree(ni,ip) +#define snmp_delete_arpidx_tree(ni,ip) + +/* IP */ +#define snmp_inc_ipinreceives() +#define snmp_inc_ipinhdrerrors() +#define snmp_inc_ipinaddrerrors() +#define snmp_inc_ipforwdatagrams() +#define snmp_inc_ipinunknownprotos() +#define snmp_inc_ipindiscards() +#define snmp_inc_ipindelivers() +#define snmp_inc_ipoutrequests() +#define snmp_inc_ipoutdiscards() +#define snmp_inc_ipoutnoroutes() +#define snmp_inc_ipreasmreqds() +#define snmp_inc_ipreasmoks() +#define snmp_inc_ipreasmfails() +#define snmp_inc_ipfragoks() +#define snmp_inc_ipfragfails() +#define snmp_inc_ipfragcreates() +#define snmp_inc_iproutingdiscards() +#define snmp_insert_ipaddridx_tree(ni) +#define snmp_delete_ipaddridx_tree(ni) +#define snmp_insert_iprteidx_tree(dflt, ni) +#define snmp_delete_iprteidx_tree(dflt, ni) + +/* ICMP */ +#define snmp_inc_icmpinmsgs() +#define snmp_inc_icmpinerrors() +#define snmp_inc_icmpindestunreachs() +#define snmp_inc_icmpintimeexcds() +#define snmp_inc_icmpinparmprobs() +#define snmp_inc_icmpinsrcquenchs() +#define snmp_inc_icmpinredirects() +#define snmp_inc_icmpinechos() +#define snmp_inc_icmpinechoreps() +#define snmp_inc_icmpintimestamps() +#define snmp_inc_icmpintimestampreps() +#define snmp_inc_icmpinaddrmasks() +#define snmp_inc_icmpinaddrmaskreps() +#define snmp_inc_icmpoutmsgs() +#define snmp_inc_icmpouterrors() +#define snmp_inc_icmpoutdestunreachs() +#define snmp_inc_icmpouttimeexcds() +#define snmp_inc_icmpoutparmprobs() +#define snmp_inc_icmpoutsrcquenchs() +#define snmp_inc_icmpoutredirects() +#define snmp_inc_icmpoutechos() +#define snmp_inc_icmpoutechoreps() +#define snmp_inc_icmpouttimestamps() +#define snmp_inc_icmpouttimestampreps() +#define snmp_inc_icmpoutaddrmasks() +#define snmp_inc_icmpoutaddrmaskreps() +/* TCP */ +#define snmp_inc_tcpactiveopens() +#define snmp_inc_tcppassiveopens() +#define snmp_inc_tcpattemptfails() +#define snmp_inc_tcpestabresets() +#define snmp_inc_tcpinsegs() +#define snmp_inc_tcpoutsegs() +#define snmp_inc_tcpretranssegs() +#define snmp_inc_tcpinerrs() +#define snmp_inc_tcpoutrsts() + +/* UDP */ +#define snmp_inc_udpindatagrams() +#define snmp_inc_udpnoports() +#define snmp_inc_udpinerrors() +#define snmp_inc_udpoutdatagrams() +#define snmp_insert_udpidx_tree(pcb) +#define snmp_delete_udpidx_tree(pcb) + +/* SNMP */ +#define snmp_inc_snmpinpkts() +#define snmp_inc_snmpoutpkts() +#define snmp_inc_snmpinbadversions() +#define snmp_inc_snmpinbadcommunitynames() +#define snmp_inc_snmpinbadcommunityuses() +#define snmp_inc_snmpinasnparseerrs() +#define snmp_inc_snmpintoobigs() +#define snmp_inc_snmpinnosuchnames() +#define snmp_inc_snmpinbadvalues() +#define snmp_inc_snmpinreadonlys() +#define snmp_inc_snmpingenerrs() +#define snmp_add_snmpintotalreqvars(value) +#define snmp_add_snmpintotalsetvars(value) +#define snmp_inc_snmpingetrequests() +#define snmp_inc_snmpingetnexts() +#define snmp_inc_snmpinsetrequests() +#define snmp_inc_snmpingetresponses() +#define snmp_inc_snmpintraps() +#define snmp_inc_snmpouttoobigs() +#define snmp_inc_snmpoutnosuchnames() +#define snmp_inc_snmpoutbadvalues() +#define snmp_inc_snmpoutgenerrs() +#define snmp_inc_snmpoutgetrequests() +#define snmp_inc_snmpoutgetnexts() +#define snmp_inc_snmpoutsetrequests() +#define snmp_inc_snmpoutgetresponses() +#define snmp_inc_snmpouttraps() +#define snmp_get_snmpgrpid_ptr(oid) +#define snmp_set_snmpenableauthentraps(value) +#define snmp_get_snmpenableauthentraps(value) + +#endif /* LWIP_SNMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SNMP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/snmp_asn1.h b/external/badvpn_dns/lwip/src/include/lwip/snmp_asn1.h new file mode 100644 index 00000000..605fa3f1 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/snmp_asn1.h @@ -0,0 +1,101 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) codec. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_ASN1_H__ +#define __LWIP_SNMP_ASN1_H__ + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" + +#if LWIP_SNMP + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */ +#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */ +#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */ + +#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */ +#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */ + +/* universal tags */ +#define SNMP_ASN1_INTEG 2 +#define SNMP_ASN1_OC_STR 4 +#define SNMP_ASN1_NUL 5 +#define SNMP_ASN1_OBJ_ID 6 +#define SNMP_ASN1_SEQ 16 + +/* application specific (SNMP) tags */ +#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */ +#define SNMP_ASN1_COUNTER 1 /* u32_t */ +#define SNMP_ASN1_GAUGE 2 /* u32_t */ +#define SNMP_ASN1_TIMETICKS 3 /* u32_t */ +#define SNMP_ASN1_OPAQUE 4 /* octet string */ + +/* context specific (SNMP) tags */ +#define SNMP_ASN1_PDU_GET_REQ 0 +#define SNMP_ASN1_PDU_GET_NEXT_REQ 1 +#define SNMP_ASN1_PDU_GET_RESP 2 +#define SNMP_ASN1_PDU_SET_REQ 3 +#define SNMP_ASN1_PDU_TRAP 4 + +err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type); +err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length); +err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value); +err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value); +err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid); +err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw); + +void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed); +void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed); +void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed); +void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed); +err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type); +err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length); +err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value); +err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident); +err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/snmp_msg.h b/external/badvpn_dns/lwip/src/include/lwip/snmp_msg.h new file mode 100644 index 00000000..1183e3a9 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/snmp_msg.h @@ -0,0 +1,315 @@ +/** + * @file + * SNMP Agent message handling structures. + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_MSG_H__ +#define __LWIP_SNMP_MSG_H__ + +#include "lwip/opt.h" +#include "lwip/snmp.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* The listen port of the SNMP agent. Clients have to make their requests to + this port. Most standard clients won't work if you change this! */ +#ifndef SNMP_IN_PORT +#define SNMP_IN_PORT 161 +#endif +/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't + work if you change this! */ +#ifndef SNMP_TRAP_PORT +#define SNMP_TRAP_PORT 162 +#endif + +#define SNMP_ES_NOERROR 0 +#define SNMP_ES_TOOBIG 1 +#define SNMP_ES_NOSUCHNAME 2 +#define SNMP_ES_BADVALUE 3 +#define SNMP_ES_READONLY 4 +#define SNMP_ES_GENERROR 5 + +#define SNMP_GENTRAP_COLDSTART 0 +#define SNMP_GENTRAP_WARMSTART 1 +#define SNMP_GENTRAP_AUTHFAIL 4 +#define SNMP_GENTRAP_ENTERPRISESPC 6 + +struct snmp_varbind +{ + /* next pointer, NULL for last in list */ + struct snmp_varbind *next; + /* previous pointer, NULL for first in list */ + struct snmp_varbind *prev; + + /* object identifier length (in s32_t) */ + u8_t ident_len; + /* object identifier array */ + s32_t *ident; + + /* object value ASN1 type */ + u8_t value_type; + /* object value length (in u8_t) */ + u8_t value_len; + /* object value */ + void *value; + + /* encoding varbind seq length length */ + u8_t seqlenlen; + /* encoding object identifier length length */ + u8_t olenlen; + /* encoding object value length length */ + u8_t vlenlen; + /* encoding varbind seq length */ + u16_t seqlen; + /* encoding object identifier length */ + u16_t olen; + /* encoding object value length */ + u16_t vlen; +}; + +struct snmp_varbind_root +{ + struct snmp_varbind *head; + struct snmp_varbind *tail; + /* number of variable bindings in list */ + u8_t count; + /* encoding varbind-list seq length length */ + u8_t seqlenlen; + /* encoding varbind-list seq length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_resp_header_lengths +{ + /* encoding error-index length length */ + u8_t erridxlenlen; + /* encoding error-status length length */ + u8_t errstatlenlen; + /* encoding request id length length */ + u8_t ridlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding error-index length */ + u16_t erridxlen; + /* encoding error-status length */ + u16_t errstatlen; + /* encoding request id length */ + u16_t ridlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/** output response message header length fields */ +struct snmp_trap_header_lengths +{ + /* encoding timestamp length length */ + u8_t tslenlen; + /* encoding specific-trap length length */ + u8_t strplenlen; + /* encoding generic-trap length length */ + u8_t gtrplenlen; + /* encoding agent-addr length length */ + u8_t aaddrlenlen; + /* encoding enterprise-id length length */ + u8_t eidlenlen; + /* encoding pdu length length */ + u8_t pdulenlen; + /* encoding community length length */ + u8_t comlenlen; + /* encoding version length length */ + u8_t verlenlen; + /* encoding sequence length length */ + u8_t seqlenlen; + + /* encoding timestamp length */ + u16_t tslen; + /* encoding specific-trap length */ + u16_t strplen; + /* encoding generic-trap length */ + u16_t gtrplen; + /* encoding agent-addr length */ + u16_t aaddrlen; + /* encoding enterprise-id length */ + u16_t eidlen; + /* encoding pdu length */ + u16_t pdulen; + /* encoding community length */ + u16_t comlen; + /* encoding version length */ + u16_t verlen; + /* encoding sequence length */ + u16_t seqlen; +}; + +/* Accepting new SNMP messages. */ +#define SNMP_MSG_EMPTY 0 +/* Search for matching object for variable binding. */ +#define SNMP_MSG_SEARCH_OBJ 1 +/* Perform SNMP operation on in-memory object. + Pass-through states, for symmetry only. */ +#define SNMP_MSG_INTERNAL_GET_OBJDEF 2 +#define SNMP_MSG_INTERNAL_GET_VALUE 3 +#define SNMP_MSG_INTERNAL_SET_TEST 4 +#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5 +#define SNMP_MSG_INTERNAL_SET_VALUE 6 +/* Perform SNMP operation on object located externally. + In theory this could be used for building a proxy agent. + Practical use is for an enterprise spc. app. gateway. */ +#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7 +#define SNMP_MSG_EXTERNAL_GET_VALUE 8 +#define SNMP_MSG_EXTERNAL_SET_TEST 9 +#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10 +#define SNMP_MSG_EXTERNAL_SET_VALUE 11 + +#define SNMP_COMMUNITY_STR_LEN 64 +struct snmp_msg_pstat +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* source IP address */ + ip_addr_t sip; + /* source UDP port */ + u16_t sp; + /* request type */ + u8_t rt; + /* request ID */ + s32_t rid; + /* error status */ + s32_t error_status; + /* error index */ + s32_t error_index; + /* community name (zero terminated) */ + u8_t community[SNMP_COMMUNITY_STR_LEN + 1]; + /* community string length (exclusive zero term) */ + u8_t com_strlen; + /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */ + u8_t state; + /* saved arguments for MSG_EXTERNAL_x */ + struct mib_external_node *ext_mib_node; + struct snmp_name_ptr ext_name_ptr; + struct obj_def ext_object_def; + struct snmp_obj_id ext_oid; + /* index into input variable binding list */ + u8_t vb_idx; + /* ptr into input variable binding list */ + struct snmp_varbind *vb_ptr; + /* list of variable bindings from input */ + struct snmp_varbind_root invb; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output response lengths used in ASN encoding */ + struct snmp_resp_header_lengths rhl; +}; + +struct snmp_msg_trap +{ + /* lwIP local port (161) binding */ + struct udp_pcb *pcb; + /* destination IP address in network order */ + ip_addr_t dip; + + /* source enterprise ID (sysObjectID) */ + struct snmp_obj_id *enterprise; + /* source IP address, raw network order format */ + u8_t sip_raw[4]; + /* generic trap code */ + u32_t gen_trap; + /* specific trap code */ + u32_t spc_trap; + /* timestamp */ + u32_t ts; + /* list of variable bindings to output */ + struct snmp_varbind_root outvb; + /* output trap lengths used in ASN encoding */ + struct snmp_trap_header_lengths thl; +}; + +/** Agent Version constant, 0 = v1 oddity */ +extern const s32_t snmp_version; +/** Agent default "public" community string */ +extern const char snmp_publiccommunity[7]; + +extern struct snmp_msg_trap trap_msg; + +/** Agent setup, start listening to port 161. */ +void snmp_init(void); +void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable); +void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst); + +/** Varbind-list functions. */ +struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len); +void snmp_varbind_free(struct snmp_varbind *vb); +void snmp_varbind_list_free(struct snmp_varbind_root *root); +void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb); +struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root); + +/** Handle an internal (recv) or external (private response) event. */ +void snmp_msg_event(u8_t request_id); +err_t snmp_send_response(struct snmp_msg_pstat *m_stat); +err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap); +void snmp_coldstart_trap(void); +void snmp_authfail_trap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_MSG_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/snmp_structs.h b/external/badvpn_dns/lwip/src/include/lwip/snmp_structs.h new file mode 100644 index 00000000..0d3b46a9 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/snmp_structs.h @@ -0,0 +1,268 @@ +/** + * @file + * Generic MIB tree structures. + * + * @todo namespace prefixes + */ + +/* + * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Christiaan Simons + */ + +#ifndef __LWIP_SNMP_STRUCTS_H__ +#define __LWIP_SNMP_STRUCTS_H__ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" + +#if SNMP_PRIVATE_MIB +/* When using a private MIB, you have to create a file 'private_mib.h' that contains + * a 'struct mib_array_node mib_private' which contains your MIB. */ +#include "private_mib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MIB object instance */ +#define MIB_OBJECT_NONE 0 +#define MIB_OBJECT_SCALAR 1 +#define MIB_OBJECT_TAB 2 + +/* MIB access types */ +#define MIB_ACCESS_READ 1 +#define MIB_ACCESS_WRITE 2 + +/* MIB object access */ +#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ +#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE) +#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE +#define MIB_OBJECT_NOT_ACCESSIBLE 0 + +/** object definition returned by (get_object_def)() */ +struct obj_def +{ + /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */ + u8_t instance; + /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */ + u8_t access; + /* ASN type for this object */ + u8_t asn_type; + /* value length (host length) */ + u16_t v_len; + /* length of instance part of supplied object identifier */ + u8_t id_inst_len; + /* instance part of supplied object identifier */ + s32_t *id_inst_ptr; +}; + +struct snmp_name_ptr +{ + u8_t ident_len; + s32_t *ident; +}; + +/** MIB const scalar (.0) node */ +#define MIB_NODE_SC 0x01 +/** MIB const array node */ +#define MIB_NODE_AR 0x02 +/** MIB array node (mem_malloced from RAM) */ +#define MIB_NODE_RA 0x03 +/** MIB list root node (mem_malloced from RAM) */ +#define MIB_NODE_LR 0x04 +/** MIB node for external objects */ +#define MIB_NODE_EX 0x05 + +/** node "base class" layout, the mandatory fields for a node */ +struct mib_node +{ + /** returns struct obj_def for the given object identifier */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + /** returns object value for the given object identifier, + @note the caller must allocate at least len bytes for the value */ + void (*get_value)(struct obj_def *od, u16_t len, void *value); + /** tests length and/or range BEFORE setting */ + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + /** sets object value, only to be called when set_test() */ + void (*set_value)(struct obj_def *od, u16_t len, void *value); + /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */ + u8_t node_type; + /* array or max list length */ + u16_t maxlength; +}; + +/** derived node for scalars .0 index */ +typedef struct mib_node mib_scalar_node; + +/** derived node, points to a fixed size const array + of sub-identifiers plus a 'child' pointer */ +struct mib_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + const s32_t *objid; + struct mib_node* const *nptr; +}; + +/** derived node, points to a fixed size mem_malloced array + of sub-identifiers plus a 'child' pointer */ +struct mib_ram_array_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* aditional struct members */ + s32_t *objid; + struct mib_node **nptr; +}; + +struct mib_list_node +{ + struct mib_list_node *prev; + struct mib_list_node *next; + s32_t objid; + struct mib_node *nptr; +}; + +/** derived node, points to a doubly linked list + of sub-identifiers plus a 'child' pointer */ +struct mib_list_rootnode +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + struct mib_list_node *head; + struct mib_list_node *tail; + /* counts list nodes in list */ + u16_t count; +}; + +/** derived node, has access functions for mib object in external memory or device + using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */ +struct mib_external_node +{ + /* inherited "base class" members */ + void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value)(struct obj_def *od, u16_t len, void *value); + u8_t (*set_test)(struct obj_def *od, u16_t len, void *value); + void (*set_value)(struct obj_def *od, u16_t len, void *value); + + u8_t node_type; + u16_t maxlength; + + /* additional struct members */ + /** points to an external (in memory) record of some sort of addressing + information, passed to and interpreted by the funtions below */ + void* addr_inf; + /** tree levels under this node */ + u8_t tree_levels; + /** number of objects at this level */ + u16_t (*level_length)(void* addr_inf, u8_t level); + /** compares object sub identifier with external id + return zero when equal, nonzero when unequal */ + s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id); + void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id); + + /** async Questions */ + void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_q)(u8_t rid, struct obj_def *od); + void (*set_test_q)(u8_t rid, struct obj_def *od); + void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Answers */ + void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od); + void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value); + /** async Panic Close (agent returns error reply, + e.g. used for external transaction cleanup) */ + void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident); + void (*get_value_pc)(u8_t rid, struct obj_def *od); + void (*set_test_pc)(u8_t rid, struct obj_def *od); + void (*set_value_pc)(u8_t rid, struct obj_def *od); +}; + +/** export MIB tree from mib2.c */ +extern const struct mib_array_node internet; + +/** dummy function pointers for non-leaf MIB nodes from mib2.c */ +void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +void noleafs_get_value(struct obj_def *od, u16_t len, void *value); +u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value); +void noleafs_set_value(struct obj_def *od, u16_t len, void *value); + +void snmp_oidtoip(s32_t *ident, ip_addr_t *ip); +void snmp_iptooid(ip_addr_t *ip, s32_t *ident); +void snmp_ifindextonetif(s32_t ifindex, struct netif **netif); +void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx); + +struct mib_list_node* snmp_mib_ln_alloc(s32_t id); +void snmp_mib_ln_free(struct mib_list_node *ln); +struct mib_list_rootnode* snmp_mib_lrn_alloc(void); +void snmp_mib_lrn_free(struct mib_list_rootnode *lrn); + +s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn); +s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn); +struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n); + +struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np); +struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); +u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident); +u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_STRUCTS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/sockets.h b/external/badvpn_dns/lwip/src/include/lwip/sockets.h new file mode 100644 index 00000000..73461374 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/sockets.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +#ifndef __LWIP_SOCKETS_H__ +#define __LWIP_SOCKETS_H__ + +#include "lwip/opt.h" + +#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include /* for size_t */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "lwip/inet6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + u8_t sin_family; + u16_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; + +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + u8_t sin6_family; /* AF_INET6 */ + u16_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ +}; +#endif /* LWIP_IPV6 */ + +struct sockaddr { + u8_t sa_len; + u8_t sa_family; +#if LWIP_IPV6 + u8_t sa_data[22]; +#else /* LWIP_IPV6 */ + u8_t sa_data[14]; +#endif /* LWIP_IPV6 */ +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +/* Socket protocol types (TCP/UDP/RAW) */ +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */ + +#define SO_DONTLINGER ((int)(~SO_LINGER)) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ +#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */ +#define SO_NO_CHECK 0x100a /* don't create UDP checksum */ + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xfff /* options for socket level */ + + +#define AF_UNSPEC 0 +#define AF_INET 2 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#endif /* LWIP_IPV6 */ +#define IPPROTO_UDPLITE 136 + +/* Flags we can use with send and recv. */ +#define MSG_PEEK 0x01 /* Peeks at an incoming message */ +#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */ +#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */ +#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */ +#define MSG_MORE 0x10 /* Sender will send more */ + + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + +#if LWIP_TCP +/* + * Options for level IPPROTO_TCP + */ +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */ +#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */ +#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */ +#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */ +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE +/* + * Options for level IPPROTO_UDPLITE + */ +#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +#endif /* LWIP_UDP && LWIP_UDPLITE*/ + + +#if LWIP_IGMP +/* + * Options and types for UDP multicast traffic handling + */ +#define IP_ADD_MEMBERSHIP 3 +#define IP_DROP_MEMBERSHIP 4 +#define IP_MULTICAST_TTL 5 +#define IP_MULTICAST_IF 6 +#define IP_MULTICAST_LOOP 7 + +typedef struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +} ip_mreq; +#endif /* LWIP_IGMP */ + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * lwip_ioctl only supports FIONREAD and FIONBIO, for now + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000UL /* no parameters */ +#define IOC_OUT 0x40000000UL /* copy out parameters */ +#define IOC_IN 0x80000000UL /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif /* !defined(FIONREAD) || !defined(FIONBIO) */ + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls: unimplemented */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +/* commands for fnctl */ +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif + +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 1 /* nonblocking I/O */ +#endif +#ifndef O_NDELAY +#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ +#endif + +#ifndef SHUT_RD + #define SHUT_RD 0 + #define SHUT_WR 1 + #define SHUT_RDWR 2 +#endif + +/* FD_SET used for lwip_select */ +#ifndef FD_SET + #undef FD_SETSIZE + /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */ + #define FD_SETSIZE MEMP_NUM_NETCONN + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + unsigned char fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif /* FD_SET */ + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#ifndef LWIP_TIMEVAL_PRIVATE +#define LWIP_TIMEVAL_PRIVATE 1 +#endif + +#if LWIP_TIMEVAL_PRIVATE +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* LWIP_TIMEVAL_PRIVATE */ + +void lwip_socket_init(void); + +int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen); +int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_shutdown(int s, int how); +int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen); +int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen); +int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen); +int lwip_close(int s); +int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen); +int lwip_listen(int s, int backlog); +int lwip_recv(int s, void *mem, size_t len, int flags); +int lwip_read(int s, void *mem, size_t len); +int lwip_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +int lwip_send(int s, const void *dataptr, size_t size, int flags); +int lwip_sendto(int s, const void *dataptr, size_t size, int flags, + const struct sockaddr *to, socklen_t tolen); +int lwip_socket(int domain, int type, int protocol); +int lwip_write(int s, const void *dataptr, size_t size); +int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, + struct timeval *timeout); +int lwip_ioctl(int s, long cmd, void *argp); +int lwip_fcntl(int s, int cmd, int val); + +#if LWIP_COMPAT_SOCKETS +#define accept(a,b,c) lwip_accept(a,b,c) +#define bind(a,b,c) lwip_bind(a,b,c) +#define shutdown(a,b) lwip_shutdown(a,b) +#define closesocket(s) lwip_close(s) +#define connect(a,b,c) lwip_connect(a,b,c) +#define getsockname(a,b,c) lwip_getsockname(a,b,c) +#define getpeername(a,b,c) lwip_getpeername(a,b,c) +#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e) +#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e) +#define listen(a,b) lwip_listen(a,b) +#define recv(a,b,c,d) lwip_recv(a,b,c,d) +#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f) +#define send(a,b,c,d) lwip_send(a,b,c,d) +#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f) +#define socket(a,b,c) lwip_socket(a,b,c) +#define select(a,b,c,d,e) lwip_select(a,b,c,d,e) +#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c) + +#if LWIP_POSIX_SOCKETS_IO_NAMES +#define read(a,b,c) lwip_read(a,b,c) +#define write(a,b,c) lwip_write(a,b,c) +#define close(s) lwip_close(s) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/stats.h b/external/badvpn_dns/lwip/src/include/lwip/stats.h new file mode 100644 index 00000000..d9112160 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/stats.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_STATS_H__ +#define __LWIP_STATS_H__ + +#include "lwip/opt.h" + +#include "lwip/mem.h" +#include "lwip/memp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_STATS + +#ifndef LWIP_STATS_LARGE +#define LWIP_STATS_LARGE 0 +#endif + +#if LWIP_STATS_LARGE +#define STAT_COUNTER u32_t +#define STAT_COUNTER_F U32_F +#else +#define STAT_COUNTER u16_t +#define STAT_COUNTER_F U16_F +#endif + +struct stats_proto { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER fw; /* Forwarded packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER rterr; /* Routing error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER opterr; /* Error in options. */ + STAT_COUNTER err; /* Misc error. */ + STAT_COUNTER cachehit; +}; + +struct stats_igmp { + STAT_COUNTER xmit; /* Transmitted packets. */ + STAT_COUNTER recv; /* Received packets. */ + STAT_COUNTER drop; /* Dropped packets. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER lenerr; /* Invalid length error. */ + STAT_COUNTER memerr; /* Out of memory error. */ + STAT_COUNTER proterr; /* Protocol error. */ + STAT_COUNTER rx_v1; /* Received v1 frames. */ + STAT_COUNTER rx_group; /* Received group-specific queries. */ + STAT_COUNTER rx_general; /* Received general queries. */ + STAT_COUNTER rx_report; /* Received reports. */ + STAT_COUNTER tx_join; /* Sent joins. */ + STAT_COUNTER tx_leave; /* Sent leaves. */ + STAT_COUNTER tx_report; /* Sent reports. */ +}; + +struct stats_mem { +#ifdef LWIP_DEBUG + const char *name; +#endif /* LWIP_DEBUG */ + mem_size_t avail; + mem_size_t used; + mem_size_t max; + STAT_COUNTER err; + STAT_COUNTER illegal; +}; + +struct stats_syselem { + STAT_COUNTER used; + STAT_COUNTER max; + STAT_COUNTER err; +}; + +struct stats_sys { + struct stats_syselem sem; + struct stats_syselem mutex; + struct stats_syselem mbox; +}; + +struct stats_ { +#if LINK_STATS + struct stats_proto link; +#endif +#if ETHARP_STATS + struct stats_proto etharp; +#endif +#if IPFRAG_STATS + struct stats_proto ip_frag; +#endif +#if IP_STATS + struct stats_proto ip; +#endif +#if ICMP_STATS + struct stats_proto icmp; +#endif +#if IGMP_STATS + struct stats_igmp igmp; +#endif +#if UDP_STATS + struct stats_proto udp; +#endif +#if TCP_STATS + struct stats_proto tcp; +#endif +#if MEM_STATS + struct stats_mem mem; +#endif +#if MEMP_STATS + struct stats_mem memp[MEMP_MAX]; +#endif +#if SYS_STATS + struct stats_sys sys; +#endif +#if IP6_STATS + struct stats_proto ip6; +#endif +#if ICMP6_STATS + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + struct stats_igmp mld6; +#endif +#if ND6_STATS + struct stats_proto nd6; +#endif +}; + +extern struct stats_ lwip_stats; + +void stats_init(void); + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \ + if (lwip_stats.x.max < lwip_stats.x.used) { \ + lwip_stats.x.max = lwip_stats.x.used; \ + } \ + } while(0) +#else /* LWIP_STATS */ +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(x) +#define STATS_INC_USED(x) +#endif /* LWIP_STATS */ + +#if TCP_STATS +#define TCP_STATS_INC(x) STATS_INC(x) +#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP") +#else +#define TCP_STATS_INC(x) +#define TCP_STATS_DISPLAY() +#endif + +#if UDP_STATS +#define UDP_STATS_INC(x) STATS_INC(x) +#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP") +#else +#define UDP_STATS_INC(x) +#define UDP_STATS_DISPLAY() +#endif + +#if ICMP_STATS +#define ICMP_STATS_INC(x) STATS_INC(x) +#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP") +#else +#define ICMP_STATS_INC(x) +#define ICMP_STATS_DISPLAY() +#endif + +#if IGMP_STATS +#define IGMP_STATS_INC(x) STATS_INC(x) +#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP") +#else +#define IGMP_STATS_INC(x) +#define IGMP_STATS_DISPLAY() +#endif + +#if IP_STATS +#define IP_STATS_INC(x) STATS_INC(x) +#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP") +#else +#define IP_STATS_INC(x) +#define IP_STATS_DISPLAY() +#endif + +#if IPFRAG_STATS +#define IPFRAG_STATS_INC(x) STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG") +#else +#define IPFRAG_STATS_INC(x) +#define IPFRAG_STATS_DISPLAY() +#endif + +#if ETHARP_STATS +#define ETHARP_STATS_INC(x) STATS_INC(x) +#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP") +#else +#define ETHARP_STATS_INC(x) +#define ETHARP_STATS_DISPLAY() +#endif + +#if LINK_STATS +#define LINK_STATS_INC(x) STATS_INC(x) +#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK") +#else +#define LINK_STATS_INC(x) +#define LINK_STATS_DISPLAY() +#endif + +#if MEM_STATS +#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y +#define MEM_STATS_INC(x) STATS_INC(mem.x) +#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y) +#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y +#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP") +#else +#define MEM_STATS_AVAIL(x, y) +#define MEM_STATS_INC(x) +#define MEM_STATS_INC_USED(x, y) +#define MEM_STATS_DEC_USED(x, y) +#define MEM_STATS_DISPLAY() +#endif + +#if MEMP_STATS +#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y +#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x) +#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x) +#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1) +#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i) +#else +#define MEMP_STATS_AVAIL(x, i, y) +#define MEMP_STATS_INC(x, i) +#define MEMP_STATS_DEC(x, i) +#define MEMP_STATS_INC_USED(x, i) +#define MEMP_STATS_DISPLAY(i) +#endif + +#if SYS_STATS +#define SYS_STATS_INC(x) STATS_INC(sys.x) +#define SYS_STATS_DEC(x) STATS_DEC(sys.x) +#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1) +#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_INC_USED(x) +#define SYS_STATS_DISPLAY() +#endif + +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp, name) +#define stats_display_mem(mem, name) +#define stats_display_memp(mem, index) +#define stats_display_sys(sys) +#endif /* LWIP_STATS_DISPLAY */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_STATS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/sys.h b/external/badvpn_dns/lwip/src/include/lwip/sys.h new file mode 100644 index 00000000..fd45ee8a --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/sys.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_SYS_H__ +#define __LWIP_SYS_H__ + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if NO_SYS + +/* For a totally minimal and standalone system, we provide null + definitions of the sys_ functions. */ +typedef u8_t sys_sem_t; +typedef u8_t sys_mutex_t; +typedef u8_t sys_mbox_t; + +#define sys_sem_new(s, c) ERR_OK +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_sem_valid(s) 0 +#define sys_sem_set_invalid(s) +#define sys_mutex_new(mu) ERR_OK +#define sys_mutex_lock(mu) +#define sys_mutex_unlock(mu) +#define sys_mutex_free(mu) +#define sys_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(mu) +#define sys_mbox_new(m, s) ERR_OK +#define sys_mbox_fetch(m,d) +#define sys_mbox_tryfetch(m,d) +#define sys_mbox_post(m,d) +#define sys_mbox_trypost(m,d) +#define sys_mbox_free(m) +#define sys_mbox_valid(m) +#define sys_mbox_set_invalid(m) + +#define sys_thread_new(n,t,a,s,p) + +#define sys_msleep(t) + +#else /* NO_SYS */ + +/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */ +#define SYS_ARCH_TIMEOUT 0xffffffffUL + +/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate. + * For now we use the same magic value, but we allow this to change in future. + */ +#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT + +#include "lwip/err.h" +#include "arch/sys_arch.h" + +/** Function prototype for thread functions */ +typedef void (*lwip_thread_fn)(void *arg); + +/* Function prototypes for functions to be implemented by platform ports + (in sys_arch.c) */ + +/* Mutex functions: */ + +/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#if LWIP_COMPAT_MUTEX +/* for old ports that don't have mutexes: define them to binary semaphores */ +#define sys_mutex_t sys_sem_t +#define sys_mutex_new(mutex) sys_sem_new(mutex, 1) +#define sys_mutex_lock(mutex) sys_sem_wait(mutex) +#define sys_mutex_unlock(mutex) sys_sem_signal(mutex) +#define sys_mutex_free(mutex) sys_sem_free(mutex) +#define sys_mutex_valid(mutex) sys_sem_valid(mutex) +#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex) + +#else /* LWIP_COMPAT_MUTEX */ + +/** Create a new mutex + * @param mutex pointer to the mutex to create + * @return a new mutex */ +err_t sys_mutex_new(sys_mutex_t *mutex); +/** Lock a mutex + * @param mutex the mutex to lock */ +void sys_mutex_lock(sys_mutex_t *mutex); +/** Unlock a mutex + * @param mutex the mutex to unlock */ +void sys_mutex_unlock(sys_mutex_t *mutex); +/** Delete a semaphore + * @param mutex the mutex to delete */ +void sys_mutex_free(sys_mutex_t *mutex); +#ifndef sys_mutex_valid +/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mutex_valid(sys_mutex_t *mutex); +#endif +#ifndef sys_mutex_set_invalid +/** Set a mutex invalid so that sys_mutex_valid returns 0 */ +void sys_mutex_set_invalid(sys_mutex_t *mutex); +#endif +#endif /* LWIP_COMPAT_MUTEX */ + +/* Semaphore functions: */ + +/** Create a new semaphore + * @param sem pointer to the semaphore to create + * @param count initial count of the semaphore + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_sem_new(sys_sem_t *sem, u8_t count); +/** Signals a semaphore + * @param sem the semaphore to signal */ +void sys_sem_signal(sys_sem_t *sem); +/** Wait for a semaphore for the specified timeout + * @param sem the semaphore to wait for + * @param timeout timeout in milliseconds to wait (0 = wait forever) + * @return time (in milliseconds) waited for the semaphore + * or SYS_ARCH_TIMEOUT on timeout */ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout); +/** Delete a semaphore + * @param sem semaphore to delete */ +void sys_sem_free(sys_sem_t *sem); +/** Wait for a semaphore - forever/no timeout */ +#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0) +#ifndef sys_sem_valid +/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_sem_valid(sys_sem_t *sem); +#endif +#ifndef sys_sem_set_invalid +/** Set a semaphore invalid so that sys_sem_valid returns 0 */ +void sys_sem_set_invalid(sys_sem_t *sem); +#endif + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif + +/* Mailbox functions. */ + +/** Create a new mbox of specified size + * @param mbox pointer to the mbox to create + * @param size (miminum) number of messages in this mbox + * @return ERR_OK if successful, another err_t otherwise */ +err_t sys_mbox_new(sys_mbox_t *mbox, int size); +/** Post a message to an mbox - may not fail + * -> blocks if full, only used from tasks not from ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +void sys_mbox_post(sys_mbox_t *mbox, void *msg); +/** Try to post a message to an mbox - may fail if full or ISR + * @param mbox mbox to posts the message + * @param msg message to post (ATTENTION: can be NULL) */ +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg); +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever) + * @return time (in milliseconds) waited for a message, may be 0 if not waited + or SYS_ARCH_TIMEOUT on timeout + * The returned time has to be accurate to prevent timer jitter! */ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout); +/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */ +#ifndef sys_arch_mbox_tryfetch +/** Wait for a new message to arrive in the mbox + * @param mbox mbox to get a message from + * @param msg pointer where the message is stored + * @return 0 (milliseconds) if a message has been received + * or SYS_MBOX_EMPTY if the mailbox is empty */ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg); +#endif +/** For now, we map straight to sys_arch implementation. */ +#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg) +/** Delete an mbox + * @param mbox mbox to delete */ +void sys_mbox_free(sys_mbox_t *mbox); +#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0) +#ifndef sys_mbox_valid +/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */ +int sys_mbox_valid(sys_mbox_t *mbox); +#endif +#ifndef sys_mbox_set_invalid +/** Set an mbox invalid so that sys_mbox_valid returns 0 */ +void sys_mbox_set_invalid(sys_mbox_t *mbox); +#endif + +/** The only thread function: + * Creates a new thread + * @param name human-readable name for the thread (used for debugging purposes) + * @param thread thread-function + * @param arg parameter passed to 'thread' + * @param stacksize stack size in bytes for the new thread (may be ignored by ports) + * @param prio priority of the new thread (may be ignored by ports) */ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/* sys_init() must be called before anthing else. */ +void sys_init(void); + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void); +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +u32_t sys_now(void); + +/* Critical Region Protection */ +/* These functions must be implemented in the sys_arch.c file. + In some implementations they can provide a more light-weight protection + mechanism than using semaphores. Otherwise semaphores can be used for + implementation */ +#ifndef SYS_ARCH_PROTECT +/** SYS_LIGHTWEIGHT_PROT + * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection + * for certain critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#if SYS_LIGHTWEIGHT_PROT + +/** SYS_ARCH_DECL_PROTECT + * declare a protection variable. This macro will default to defining a variable of + * type sys_prot_t. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h. + */ +#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev +/** SYS_ARCH_PROTECT + * Perform a "fast" protect. This could be implemented by + * disabling interrupts for an embedded system or by using a semaphore or + * mutex. The implementation should allow calling SYS_ARCH_PROTECT when + * already protected. The old protection level is returned in the variable + * "lev". This macro will default to calling the sys_arch_protect() function + * which should be implemented in sys_arch.c. If a particular port needs a + * different implementation, then this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect() +/** SYS_ARCH_UNPROTECT + * Perform a "fast" set of the protection level to "lev". This could be + * implemented by setting the interrupt level to "lev" within the MACRO or by + * using a semaphore or mutex. This macro will default to calling the + * sys_arch_unprotect() function which should be implemented in + * sys_arch.c. If a particular port needs a different implementation, then + * this macro may be defined in sys_arch.h + */ +#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev) +sys_prot_t sys_arch_protect(void); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#endif /* SYS_ARCH_PROTECT */ + +/* + * Macros to set/get and increase/decrease variables in a thread-safe way. + * Use these for accessing variable that are used from more than one thread. + */ + +#ifndef SYS_ARCH_INC +#define SYS_ARCH_INC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var += val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_INC */ + +#ifndef SYS_ARCH_DEC +#define SYS_ARCH_DEC(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var -= val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_DEC */ + +#ifndef SYS_ARCH_GET +#define SYS_ARCH_GET(var, ret) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + ret = var; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_GET */ + +#ifndef SYS_ARCH_SET +#define SYS_ARCH_SET(var, val) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + var = val; \ + SYS_ARCH_UNPROTECT(old_level); \ + } while(0) +#endif /* SYS_ARCH_SET */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_SYS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/tcp.h b/external/badvpn_dns/lwip/src/include/lwip/tcp.h new file mode 100644 index 00000000..535b6a38 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/tcp.h @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_H__ +#define __LWIP_TCP_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +/** Function prototype for tcp accept callback functions. Called when a new + * connection can be accepted on a listening pcb. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param newpcb The new connection pcb + * @param err An error code if there has been an error accepting. + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err); + +/** Function prototype for tcp receive callback functions. Called when data has + * been received. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which received data + * @param p The received data (or NULL when the connection has been closed!) + * @param err An error code if there has been an error receiving + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err); + +/** Function prototype for tcp sent callback functions. Called when sent data has + * been acknowledged by the remote side. Use it to free corresponding resources. + * This also means that the pcb has now space available to send new data. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb for which data has been acknowledged + * @param len The amount of bytes acknowledged + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb, + u16_t len); + +/** Function prototype for tcp poll callback functions. Called periodically as + * specified by @see tcp_poll. + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb tcp pcb + * @return ERR_OK: try to send some data by calling tcp_output + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + */ +typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb); + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +typedef void (*tcp_err_fn)(void *arg, err_t err); + +/** Function prototype for tcp connected callback functions. Called when a pcb + * is connected to the remote side after initiating a connection attempt by + * calling tcp_connect(). + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param tpcb The connection pcb which is connected + * @param err An unused error code, always ERR_OK currently ;-) TODO! + * Only return ERR_ABRT if you have called tcp_abort from within the + * callback function! + * + * @note When a connection attempt fails, the error callback is currently called! + */ +typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err); + +enum tcp_state { + CLOSED = 0, + LISTEN = 1, + SYN_SENT = 2, + SYN_RCVD = 3, + ESTABLISHED = 4, + FIN_WAIT_1 = 5, + FIN_WAIT_2 = 6, + CLOSE_WAIT = 7, + CLOSING = 8, + LAST_ACK = 9, + TIME_WAIT = 10 +}; + +#if LWIP_CALLBACK_API + /* Function to call when a listener has been connected. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb a new tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: accept the new connection, + * any other err_t abortsthe new connection + */ +#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept; +#else /* LWIP_CALLBACK_API */ +#define DEF_ACCEPT_CALLBACK +#endif /* LWIP_CALLBACK_API */ + +/** + * members common to struct tcp_pcb and struct tcp_listen_pcb + */ +#define TCP_PCB_COMMON(type) \ + type *next; /* for the linked list */ \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* ports are in host byte order */ \ + int bound_to_netif; \ + u16_t local_port; \ + char local_netif[3] + + +/* the TCP protocol control block */ +struct tcp_pcb { +/** common PCB members */ + IP_PCB; +/** protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb); + + /* ports are in host byte order */ + u16_t remote_port; + + u8_t flags; +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + u16_t rcv_wnd; /* receiver window available */ + u16_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* Retransmission timer. */ + s16_t rtime; + + u16_t mss; /* maximum segment size */ + + /* RTT (round trip time) estimation variables */ + u32_t rttest; /* RTT estimate in 500ms ticks */ + u32_t rtseq; /* sequence number being timed */ + s16_t sa, sv; /* @todo document this */ + + s16_t rto; /* retransmission time-out */ + u8_t nrtx; /* number of retransmissions */ + + /* fast retransmit/recovery */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last + window update. */ + u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ + u16_t snd_wnd; /* sender window */ + u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) + u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */ + +#if TCP_OVERSIZE + /* Extra bytes available at the end of the last pbuf in unsent. */ + u16_t unsent_oversize; +#endif /* TCP_OVERSIZE */ + + /* These are ordered by sequence number: */ + struct tcp_seg *unsent; /* Unsent (queued) segments. */ + struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ +#if TCP_QUEUE_OOSEQ + struct tcp_seg *ooseq; /* Received out of sequence segments. */ +#endif /* TCP_QUEUE_OOSEQ */ + + struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ + +#if LWIP_CALLBACK_API + /* Function to be called when more send buffer space is available. */ + tcp_sent_fn sent; + /* Function to be called when (in-sequence) data has arrived. */ + tcp_recv_fn recv; + /* Function to be called when a connection has been set up. */ + tcp_connected_fn connected; + /* Function which is called periodically. */ + tcp_poll_fn poll; + /* Function to be called whenever a fatal error occurs. */ + tcp_err_fn errf; +#endif /* LWIP_CALLBACK_API */ + +#if LWIP_TCP_TIMESTAMPS + u32_t ts_lastacksent; + u32_t ts_recent; +#endif /* LWIP_TCP_TIMESTAMPS */ + + /* idle time before KEEPALIVE is sent */ + u32_t keep_idle; +#if LWIP_TCP_KEEPALIVE + u32_t keep_intvl; + u32_t keep_cnt; +#endif /* LWIP_TCP_KEEPALIVE */ + + /* Persist timer counter */ + u8_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; +}; + +struct tcp_pcb_listen { +/* Common members of all PCB types */ + IP_PCB; +/* Protocol specific PCB members */ + TCP_PCB_COMMON(struct tcp_pcb_listen); + +#if TCP_LISTEN_BACKLOG + u8_t backlog; + u8_t accepts_pending; +#endif /* TCP_LISTEN_BACKLOG */ +#if LWIP_IPV6 + u8_t accept_any_ip_version; +#endif /* LWIP_IPV6 */ +}; + +#if LWIP_EVENT_API + +enum lwip_event { + LWIP_EVENT_ACCEPT, + LWIP_EVENT_SENT, + LWIP_EVENT_RECV, + LWIP_EVENT_CONNECTED, + LWIP_EVENT_POLL, + LWIP_EVENT_ERR +}; + +err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb, + enum lwip_event, + struct pbuf *p, + u16_t size, + err_t err); + +#endif /* LWIP_EVENT_API */ + +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); + +#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen) +#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY) +#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY) +#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0) + +#if TCP_LISTEN_BACKLOG +#define tcp_accepted(pcb) do { \ + LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \ + (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \ + (pcb)->state == LISTEN) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t tcp_bind_to_netif (struct tcp_pcb *pcb, const char ifname[3]); +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* Flags for "apiflags" parameter in tcp_write */ +#define TCP_WRITE_FLAG_COPY 0x01 +#define TCP_WRITE_FLAG_MORE 0x02 + +err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + +#if LWIP_IPV6 +struct tcp_pcb * tcp_new_ip6 (void); +#define tcp_bind_ip6(pcb, ip6addr, port) \ + tcp_bind(pcb, ip6_2_ip(ip6addr), port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected) +struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) +#else /* LWIP_IPV6 */ +#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog) +#define tcp_listen_dual(pcb) tcp_listen(pcb) +#endif /* LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/tcp_impl.h b/external/badvpn_dns/lwip/src/include/lwip/tcp_impl.h new file mode 100644 index 00000000..2afc20d3 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/tcp_impl.h @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCP_IMPL_H__ +#define __LWIP_TCP_IMPL_H__ + +#include "lwip/opt.h" + +#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/tcp.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * This is the Nagle algorithm: try to combine user data to send as few TCP + * segments as possible. Only send if + * - no previously transmitted data on the connection remains unacknowledged or + * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or + * - the only unsent segment is at least pcb->mss bytes long (or there is more + * than one unsent segment - with lwIP, this can happen although unsent->len < mss) + * - or if we are in fast-retransmit (TF_INFR) + */ +#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ + ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ + (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ + ((tpcb)->unsent->len >= (tpcb)->mss))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 1 : 0) +#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) + + +#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0) +/* is b<=a<=c? */ +#if 0 /* see bug #10548 */ +#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b)) +#endif +#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) +#define TCP_FIN 0x01U +#define TCP_SYN 0x02U +#define TCP_RST 0x04U +#define TCP_PSH 0x08U +#define TCP_ACK 0x10U +#define TCP_URG 0x20U +#define TCP_ECE 0x40U +#define TCP_CWR 0x80U + +#define TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define TCP_HLEN 20 + +#ifndef TCP_TMR_INTERVAL +#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */ +#endif /* TCP_TMR_INTERVAL */ + +#ifndef TCP_FAST_INTERVAL +#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */ +#endif /* TCP_FAST_INTERVAL */ + +#ifndef TCP_SLOW_INTERVAL +#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */ +#endif /* TCP_SLOW_INTERVAL */ + +#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */ + +#ifndef TCP_MSL +#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */ +#endif + +/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */ +#ifndef TCP_KEEPIDLE_DEFAULT +#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */ +#endif + +#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */ + +/* Fields are (of course) in network byte order. + * Some fields are converted to host byte order in tcp_input(). + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) +#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags)) +#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags)) + +#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) +#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) ) + +#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0)) + +/** Flags used on input processing, not on pcb->flags +*/ +#define TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ + + +#if LWIP_EVENT_API + +#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_ACCEPT, NULL, 0, err) +#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_SENT, NULL, space, ERR_OK) +#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, (p), 0, (err)) +#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_RECV, NULL, 0, ERR_OK) +#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_CONNECTED, NULL, 0, (err)) +#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\ + LWIP_EVENT_POLL, NULL, 0, ERR_OK) +#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \ + LWIP_EVENT_ERR, NULL, 0, (err)) + +#else /* LWIP_EVENT_API */ + +#define TCP_EVENT_ACCEPT(pcb,err,ret) \ + do { \ + if((pcb)->accept != NULL) \ + (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_ARG; \ + } while (0) + +#define TCP_EVENT_SENT(pcb,space,ret) \ + do { \ + if((pcb)->sent != NULL) \ + (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_RECV(pcb,p,err,ret) \ + do { \ + if((pcb)->recv != NULL) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ + } else { \ + (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \ + } \ + } while (0) + +#define TCP_EVENT_CLOSED(pcb,ret) \ + do { \ + if(((pcb)->recv != NULL)) { \ + (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\ + } else { \ + (ret) = ERR_OK; \ + } \ + } while (0) + +#define TCP_EVENT_CONNECTED(pcb,err,ret) \ + do { \ + if((pcb)->connected != NULL) \ + (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_POLL(pcb,ret) \ + do { \ + if((pcb)->poll != NULL) \ + (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \ + else (ret) = ERR_OK; \ + } while (0) + +#define TCP_EVENT_ERR(errf,arg,err) \ + do { \ + if((errf) != NULL) \ + (errf)((arg),(err)); \ + } while (0) + +#endif /* LWIP_EVENT_API */ + +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ +#if TCP_OVERSIZE && defined(LWIP_DEBUG) +#define TCP_OVERSIZE_DBGCHECK 1 +#else +#define TCP_OVERSIZE_DBGCHECK 0 +#endif + +/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + +/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ +struct tcp_seg { + struct tcp_seg *next; /* used when putting segements on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + u16_t len; /* the TCP length of this segment */ +#if TCP_OVERSIZE_DBGCHECK + u16_t oversize_left; /* Extra bytes available at the end of the last + pbuf in unsent (used for asserting vs. + tcp_pcb.unsent_oversized only) */ +#endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ + u8_t flags; +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \ + (flags & TF_SEG_OPTS_TS ? 12 : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* The TCP PCB lists. */ +union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */ + struct tcp_pcb_listen *listen_pcbs; + struct tcp_pcb *pcbs; +}; +extern struct tcp_pcb *tcp_bound_pcbs; +extern union tcp_listen_pcbs_t tcp_listen_pcbs; +extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a + state in which they accept or send + data. */ +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ + +extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */ + +/* Axioms about the above lists: + 1) Every TCP PCB that is not CLOSED is in one of the lists. + 2) A PCB is only in one of the lists. + 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state. + 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state. +*/ +/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB + with a PCB list or removes a PCB from a list, respectively. */ +#ifndef TCP_DEBUG_PCB_LISTS +#define TCP_DEBUG_PCB_LISTS 0 +#endif +#if TCP_DEBUG_PCB_LISTS +#define TCP_REG(pcbs, npcb) do {\ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \ + for(tcp_tmp_pcb = *(pcbs); \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \ + } \ + LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \ + (npcb)->next = *(pcbs); \ + LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \ + *(pcbs) = (npcb); \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + tcp_timer_needed(); \ + } while(0) +#define TCP_RMV(pcbs, npcb) do { \ + LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \ + if(*(pcbs) == (npcb)) { \ + *(pcbs) = (*pcbs)->next; \ + } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + (npcb)->next = NULL; \ + LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \ + LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \ + } while(0) + +#else /* LWIP_DEBUG */ + +#define TCP_REG(pcbs, npcb) \ + do { \ + (npcb)->next = *pcbs; \ + *(pcbs) = (npcb); \ + tcp_timer_needed(); \ + } while (0) + +#define TCP_RMV(pcbs, npcb) \ + do { \ + if(*(pcbs) == (npcb)) { \ + (*(pcbs)) = (*pcbs)->next; \ + } \ + else { \ + for(tcp_tmp_pcb = *pcbs; \ + tcp_tmp_pcb != NULL; \ + tcp_tmp_pcb = tcp_tmp_pcb->next) { \ + if(tcp_tmp_pcb->next == (npcb)) { \ + tcp_tmp_pcb->next = (npcb)->next; \ + break; \ + } \ + } \ + } \ + (npcb)->next = NULL; \ + } while(0) + +#endif /* LWIP_DEBUG */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port) +#endif /* LWIP_IPV6 */ + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 */ +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + +#if LWIP_CALLBACK_API +err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#else +# define tcp_debug_print(tcphdr) +# define tcp_debug_print_flags(flags) +# define tcp_debug_print_state(s) +# define tcp_debug_print_pcbs() +# define tcp_pcbs_sane() 1 +#endif /* TCP_DEBUG */ + +/** External function (implemented in timers.c), called when TCP detects + * that a timer is needed (i.e. active- or time-wait-pcb found). */ +void tcp_timer_needed(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/tcpip.h b/external/badvpn_dns/lwip/src/include/lwip/tcpip.h new file mode 100644 index 00000000..04567f2c --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/tcpip.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_TCPIP_H__ +#define __LWIP_TCPIP_H__ + +#include "lwip/opt.h" + +#if !NO_SYS /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/api_msg.h" +#include "lwip/netifapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/timers.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. */ +#ifndef LWIP_TCPIP_THREAD_ALIVE +#define LWIP_TCPIP_THREAD_ALIVE() +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_mutex_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core) +#ifdef LWIP_DEBUG +#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */ +#else +#define TCIP_APIMSG_SET_ERR(m, e) +#endif +#define TCPIP_APIMSG_NOERR(m,f) do { \ + TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + LOCK_TCPIP_CORE(); \ + f(&((m)->msg)); \ + UNLOCK_TCPIP_CORE(); \ +} while(0) +#define TCPIP_APIMSG(m,f,e) do { \ + TCPIP_APIMSG_NOERR(m,f); \ + (e) = (m)->msg.err; \ +} while(0) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** Function prototype for the init_done function passed to tcpip_init */ +typedef void (*tcpip_init_done_fn)(void *arg); +/** Function prototype for functions passed to tcpip_callback() */ +typedef void (*tcpip_callback_fn)(void *ctx); + +/* Forward declarations */ +struct tcpip_callback_msg; + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +#if LWIP_NETCONN +err_t tcpip_apimsg(struct api_msg *apimsg); +#endif /* LWIP_NETCONN */ + +err_t tcpip_input(struct pbuf *p, struct netif *inp); + +#if LWIP_NETIF_API +err_t tcpip_netifapi(struct netifapi_msg *netifapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_NETIF_API */ + +err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block); +#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* free pbufs or heap memory from another context without blocking */ +err_t pbuf_free_callback(struct pbuf *p); +err_t mem_free_callback(void *m); + +#if LWIP_TCPIP_TIMEOUT +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); +#endif /* LWIP_TCPIP_TIMEOUT */ + +enum tcpip_msg_type { +#if LWIP_NETCONN + TCPIP_MSG_API, +#endif /* LWIP_NETCONN */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ +#if LWIP_TCPIP_TIMEOUT + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN + struct api_msg *apimsg; +#endif /* LWIP_NETCONN */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ + struct { + struct pbuf *p; + struct netif *netif; + } inp; + struct { + tcpip_callback_fn function; + void *ctx; + } cb; +#if LWIP_TCPIP_TIMEOUT + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; +#endif /* LWIP_TCPIP_TIMEOUT */ + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/timers.h b/external/badvpn_dns/lwip/src/include/lwip/timers.h new file mode 100644 index 00000000..04e78e0f --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/timers.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ +#ifndef __LWIP_TIMERS_H__ +#define __LWIP_TIMERS_H__ + +#include "lwip/opt.h" + +/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */ +#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) + +#if LWIP_TIMERS + +#include "lwip/err.h" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef LWIP_DEBUG_TIMERNAMES +#ifdef LWIP_DEBUG +#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG +#else /* LWIP_DEBUG */ +#define LWIP_DEBUG_TIMERNAMES 0 +#endif /* LWIP_DEBUG*/ +#endif + +/** Function prototype for a timeout callback function. Register such a function + * using sys_timeout(). + * + * @param arg Additional argument to pass to the function - set up by sys_timeout() + */ +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +#if LWIP_DEBUG_TIMERNAMES + const char* handler_name; +#endif /* LWIP_DEBUG_TIMERNAMES */ +}; + +void sys_timeouts_init(void); + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler) +#else /* LWIP_DEBUG_TIMERNAMES */ +void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg); +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg); +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +#else /* NO_SYS */ +void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg); +#endif /* NO_SYS */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TIMERS */ +#endif /* __LWIP_TIMERS_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/lwip/udp.h b/external/badvpn_dns/lwip/src/include/lwip/udp.h new file mode 100644 index 00000000..14d5c0ae --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/lwip/udp.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __LWIP_UDP_H__ +#define __LWIP_UDP_H__ + +#include "lwip/opt.h" + +#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/ip_addr.h" +#include "lwip/ip.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDP_HLEN 8 + +/* Fields are (of course) in network byte order. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct udp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */ + PACK_STRUCT_FIELD(u16_t len); + PACK_STRUCT_FIELD(u16_t chksum); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define UDP_FLAGS_NOCHKSUM 0x01U +#define UDP_FLAGS_UDPLITE 0x02U +#define UDP_FLAGS_CONNECTED 0x04U +#define UDP_FLAGS_MULTICAST_LOOP 0x08U + +struct udp_pcb; + +/** Function prototype for udp pcb receive callback functions + * addr and port are in same byte order as in the pcb + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf + * makes 'addr' invalid, too. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *addr, u16_t port); + +#if LWIP_IPV6 +/** Function prototype for udp pcb IPv6 receive callback functions + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @param arg user supplied argument (udp_pcb.recv_arg) + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IPv6 address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr, u16_t port); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6; +#else +#define UDP_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct udp_pcb { +/* Common members of all PCB types */ + IP_PCB; + +/* Protocol specific PCB members */ + + struct udp_pcb *next; + + u8_t flags; + /** ports are in host byte order */ + u16_t local_port, remote_port; + +#if LWIP_IGMP + /** outgoing network interface for multicast packets */ + ip_addr_t multicast_ip; +#endif /* LWIP_IGMP */ + +#if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; +#endif /* LWIP_UDPLITE */ + + /** receive callback function */ + union { + udp_recv_fn ip4; + UDP_PCB_RECV_IP6 + }recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for exernal reference (e.g. SNMP agent) */ +extern struct udp_pcb *udp_pcbs; + +/* The following functions is the application layer interface to the + UDP code. */ +struct udp_pcb * udp_new (void); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, u8_t have_chksum, + u16_t chksum); +err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + u8_t have_chksum, u16_t chksum); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +#define udp_flags(pcb) ((pcb)->flags) +#define udp_setflags(pcb, f) ((pcb)->flags = (f)) + +/* The following functions are the lower layer interface to UDP. */ +void udp_input (struct pbuf *p, struct netif *inp); + +void udp_init (void); + +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, ip6_2_ip(ip6addr), port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, ip6_2_ip(ip6addr), port) +#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \ + udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg) +#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \ + udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif) +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \ + udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum) +#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \ + udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum) +#endif /*LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/netif/etharp.h b/external/badvpn_dns/lwip/src/include/netif/etharp.h new file mode 100644 index 00000000..8275a28f --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/netif/etharp.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#ifndef __NETIF_ETHARP_H__ +#define __NETIF_ETHARP_H__ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/ip.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ETHARP_HWADDR_LEN +#define ETHARP_HWADDR_LEN 6 +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** Ethernet header */ +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FIELD(struct eth_addr dest); + PACK_STRUCT_FIELD(struct eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE) + +#if ETHARP_SUPPORT_VLAN + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** VLAN header inserted between ethernet header and payload + * if 'type' in ethernet header is ETHTYPE_VLAN. + * See IEEE802.Q */ +struct eth_vlan_hdr { + PACK_STRUCT_FIELD(u16_t prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_VLAN_HDR 4 +#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF) + +#endif /* ETHARP_SUPPORT_VLAN */ + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** the ARP message, see RFC 826 ("Packet format") */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u8_t hwlen); + PACK_STRUCT_FIELD(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct eth_addr shwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define SIZEOF_ETHARP_HDR 28 +#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_IPV6 0x86DDU +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables + * or known to be 32-bit aligned within the protocol header. */ +#ifndef ETHADDR32_COPY +#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local + * variables and known to be 16-bit aligned within the protocol header. */ +#ifndef ETHADDR16_COPY +#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN) +#endif + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) + * to a filter function that returns the correct netif when using multiple + * netifs on one hardware interface where the netif's low-level receive + * routine cannot decide for the correct netif (e.g. when mapping multiple + * IP addresses to one hardware interface). + */ +#ifndef LWIP_ARP_FILTER_NETIF +#define LWIP_ARP_FILTER_NETIF 0 +#endif + +#if ARP_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct etharp_q_entry { + struct etharp_q_entry *next; + struct pbuf *p; +}; +#endif /* ARP_QUEUEING */ + +#define etharp_init() /* Compatibility define, not init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); +/** For Ethernet network interfaces, we might want to send "gratuitous ARP"; + * this is an ARP packet sent by a node in order to spontaneously cause other + * nodes to update an entry in their ARP cache. + * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */ +#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr) +void etharp_cleanup_netif(struct netif *netif); + +#if ETHARP_SUPPORT_STATIC_ENTRIES +err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr); +err_t etharp_remove_static_entry(ip_addr_t *ipaddr); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_AUTOIP +err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#endif /* __NETIF_ARP_H__ */ diff --git a/external/badvpn_dns/lwip/src/include/netif/ppp_oe.h b/external/badvpn_dns/lwip/src/include/netif/ppp_oe.h new file mode 100644 index 00000000..e1cdfa51 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/netif/ppp_oe.h @@ -0,0 +1,190 @@ +/***************************************************************************** +* ppp_oe.h - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT > 0 + +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FIELD(u8_t vertype); + PACK_STRUCT_FIELD(u8_t code); + PACK_STRUCT_FIELD(u16_t session); + PACK_STRUCT_FIELD(u16_t plen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoetag { + PACK_STRUCT_FIELD(u16_t tag); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + + +#define PPPOE_STATE_INITIAL 0 +#define PPPOE_STATE_PADI_SENT 1 +#define PPPOE_STATE_PADR_SENT 2 +#define PPPOE_STATE_SESSION 3 +#define PPPOE_STATE_CLOSING 4 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_HEADERLEN sizeof(struct pppoehdr) +#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */ + +#define PPPOE_TAG_EOL 0x0000 /* end of list */ +#define PPPOE_TAG_SNAME 0x0101 /* service name */ +#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */ +#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */ +#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */ +#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */ +#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */ +#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */ +#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */ +#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */ + +#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */ +#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */ +#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */ +#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */ +#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */ + +#ifndef ETHERMTU +#define ETHERMTU 1500 +#endif + +/* two byte PPP protocol discriminator, then IP data */ +#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2) + +#ifndef PPPOE_MAX_AC_COOKIE_LEN +#define PPPOE_MAX_AC_COOKIE_LEN 64 +#endif + +struct pppoe_softc { + struct pppoe_softc *next; + struct netif *sc_ethif; /* ethernet interface we are using */ + int sc_pd; /* ppp unit number */ + void (*sc_linkStatusCB)(int pd, int up); + + int sc_state; /* discovery phase or session connected */ + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + +#ifdef PPPOE_TODO + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ +#endif /* PPPOE_TODO */ + u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */ + size_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + size_t sc_hunique_len; /* length of host unique */ +#endif + int sc_padi_retried; /* number of PADI retries already done */ + int sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct netif *ifp); + +int pppoe_connect(struct pppoe_softc *sc); +void pppoe_disconnect(struct pppoe_softc *sc); + +void pppoe_disc_input(struct netif *netif, struct pbuf *p); +void pppoe_data_input(struct netif *netif, struct pbuf *p); + +err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb); + +/** used in ppp.c */ +#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN) + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/external/badvpn_dns/lwip/src/include/netif/slipif.h b/external/badvpn_dns/lwip/src/include/netif/slipif.h new file mode 100644 index 00000000..7b6ce5e2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/netif/slipif.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __NETIF_SLIPIF_H__ +#define __NETIF_SLIPIF_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" + +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serila line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/external/badvpn_dns/lwip/src/include/posix/netdb.h b/external/badvpn_dns/lwip/src/include/posix/netdb.h new file mode 100644 index 00000000..7134032d --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/netdb.h" diff --git a/external/badvpn_dns/lwip/src/include/posix/sys/socket.h b/external/badvpn_dns/lwip/src/include/posix/sys/socket.h new file mode 100644 index 00000000..f7c7066e --- /dev/null +++ b/external/badvpn_dns/lwip/src/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/sockets.h" diff --git a/external/badvpn_dns/lwip/src/netif/FILES b/external/badvpn_dns/lwip/src/netif/FILES new file mode 100644 index 00000000..099dbf3e --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/FILES @@ -0,0 +1,29 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The PPP stack has been ported from ucip (http://ucip.sourceforge.net). + It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although + compared to that, it has some modifications for embedded systems and + the source code has been reordered a bit. \ No newline at end of file diff --git a/external/badvpn_dns/lwip/src/netif/etharp.c b/external/badvpn_dns/lwip/src/netif/etharp.c new file mode 100644 index 00000000..1b7eb988 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/etharp.c @@ -0,0 +1,1413 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_gratuitous(our_netif) upon address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" + +#if LWIP_ARP || LWIP_ETHERNET + +#include "lwip/ip_addr.h" +#include "lwip/def.h" +#include "lwip/ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; + +/** The 24-bit IANA multicast OUI is 01-00-5e: */ +#define LL_MULTICAST_ADDR_0 0x01 +#define LL_MULTICAST_ADDR_1 0x00 +#define LL_MULTICAST_ADDR_2 0x5e + +#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */ + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 5000, this is + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) + +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 5000, this is + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** Pointer to queue of pending outgoing packets on this ARP entry. */ + struct etharp_q_entry *q; +#else /* ARP_QUEUEING */ + /** Pointer to a single pending outgoing packet on this ARP entry. */ + struct pbuf *q; +#endif /* ARP_QUEUEING */ + ip_addr_t ipaddr; + struct netif *netif; + struct eth_addr ethaddr; + u8_t state; + u8_t ctime; +}; + +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif /* !LWIP_NETIF_HWADDRHINT */ + +/** Try hard to create a new entry - we want the IP address to appear in + the cache (even if this means removing an active entry or so). */ +#define ETHARP_FLAG_TRY_HARD 1 +#define ETHARP_FLAG_FIND_ONLY 2 +#if ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#if LWIP_NETIF_HWADDRHINT +#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +#else /* LWIP_NETIF_HWADDRHINT */ +#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) +#endif /* LWIP_NETIF_HWADDRHINT */ + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h" +#endif + + +#if ARP_QUEUEING +/** + * Free a complete queue of etharp entries + * + * @param q a qeueue of etharp_q_entry's to free + */ +static void +free_etharp_q(struct etharp_q_entry *q) +{ + struct etharp_q_entry *r; + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("q->p != NULL", q->p != NULL); + while (q) { + r = q; + q = q->next; + LWIP_ASSERT("r->p != NULL", (r->p != NULL)); + pbuf_free(r->p); + memp_free(MEMP_ARP_QUEUE, r); + } +} +#else /* ARP_QUEUEING */ + +/** Compatibility define: free the queued pbuf */ +#define free_etharp_q(q) pbuf_free(q) + +#endif /* ARP_QUEUEING */ + +/** Clean up ARP table entries */ +static void +etharp_free_entry(int i) +{ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + /* and empty packet queue */ + if (arp_table[i].q != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; + } + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; + arp_table[i].netif = NULL; + ip_addr_set_zero(&arp_table[i].ipaddr); + arp_table[i].ethaddr = ethzero; +#endif /* LWIP_DEBUG */ +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if (state != ETHARP_STATE_EMPTY +#if ETHARP_SUPPORT_STATIC_ENTRIES + && (state != ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + ) { + arp_table[i].ctime++; + if ((arp_table[i].ctime >= ARP_MAXAGE) || + ((arp_table[i].state == ETHARP_STATE_PENDING) && + (arp_table[i].ctime >= ARP_MAXPENDING))) { + /* pending or stable entry has become old! */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n", + arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i)); + /* clean up entries that have just been expired */ + etharp_free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif /* ARP_QUEUEING */ + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags @see definition of ETHARP_FLAG_* + * @param netif netif related to this address (used for NETIF_HWADDRHINT) + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t +etharp_find_entry(ip_addr_t *ipaddr, u8_t flags) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } else if (state != ETHARP_STATE_EMPTY) { + LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE", + state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE); + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + } + /* pending entry? */ + if (state == ETHARP_STATE_PENDING) { + /* pending with queued packets? */ + if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } + } else + /* pending without queued packets? */ + { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + /* stable entry? */ + } else if (state >= ETHARP_STATE_STABLE) { +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* don't record old_stable for static entries since they never expire */ + if (state < ETHARP_STATE_STATIC) +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + } + } + /* { we have no match } => try to create a new entry */ + + /* don't create new entry, only search? */ + if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || + /* or no empty entry found and not allowed to recycle? */ + ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n")); + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry with queued packets + * + * { ETHARP_FLAG_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } else { + /* 2) found recyclable stable entry? */ + if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending (queued packets are free in etharp_free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q))); + /* no empty or recyclable entries found */ + } else { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n")); + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + etharp_free_entry(i); + } + + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY", + arp_table[i].state == ETHARP_STATE_EMPTY); + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_copy(arp_table[i].ipaddr, *ipaddr); + } + arp_table[i].ctime = 0; + return (err_t)i; +} + +/** + * Send an IP packet on the network using netif->linkoutput + * The ethernet header is filled in before sending. + * + * @params netif the lwIP network interface on which to send the packet + * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header + * @params src the source MAC address to be copied into the ethernet header + * @params dst the destination MAC address to be copied into the ethernet header + * @return ERR_OK if the packet was sent, any other err_t on failure + */ +static err_t +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param netif netif related to this entry (used for NETIF_ADDRHINT) + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags @see definition of ETHARP_FLAG_* + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t +etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, flags); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + +#if ETHARP_SUPPORT_STATIC_ENTRIES + if (flags & ETHARP_FLAG_STATIC_ENTRY) { + /* record static type */ + arp_table[i].state = ETHARP_STATE_STATIC; + } else +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + + /* record network interface */ + arp_table[i].netif = netif; + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); + /* reset time stamp */ + arp_table[i].ctime = 0; + /* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].q != NULL) { + struct pbuf *p; + /* remember remainder of queue */ + struct etharp_q_entry *q = arp_table[i].q; + /* pop first item off the queue */ + arp_table[i].q = q->next; + /* get the packet pointer */ + p = q->p; + /* now queue entry can be freed */ + memp_free(MEMP_ARP_QUEUE, q); +#else /* ARP_QUEUEING */ + if (arp_table[i].q != NULL) { + struct pbuf *p = arp_table[i].q; + arp_table[i].q = NULL; +#endif /* ARP_QUEUEING */ + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } + return ERR_OK; +} + +#if ETHARP_SUPPORT_STATIC_ENTRIES +/** Add a new static entry to the ARP table. If an entry exists for the + * specified IP address, this entry is overwritten. + * If packets are queued for the specified IP address, they are sent out. + * + * @param ipaddr IP address for the new static entry + * @param ethaddr ethernet address for the new static entry + * @return @see return values of etharp_add_static_entry + */ +err_t +etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) +{ + struct netif *netif; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + + netif = ip_route(ipaddr); + if (netif == NULL) { + return ERR_RTE; + } + + return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); +} + +/** Remove a static entry from the ARP table previously added with a call to + * etharp_add_static_entry. + * + * @param ipaddr IP address of the static entry to remove + * @return ERR_OK: entry removed + * ERR_MEM: entry wasn't found + * ERR_ARG: entry wasn't a static entry but a dynamic one + */ +err_t +etharp_remove_static_entry(ip_addr_t *ipaddr) +{ + s8_t i; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); + + /* find or create ARP entry */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + /* bail out if no entry could be found */ + if (i < 0) { + return (err_t)i; + } + + if (arp_table[i].state != ETHARP_STATE_STATIC) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + etharp_free_entry(i); + return ERR_OK; +} +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +/** + * Remove all ARP table entries of the specified netif. + * + * @param netif points to a network interface + */ +void etharp_cleanup_netif(struct netif *netif) +{ + u8_t i; + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + u8_t state = arp_table[i].state; + if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { + etharp_free_entry(i); + } + } +} + +/** + * Finds (stable) ethernet/IP address pair from ARP table + * using interface and IP address index. + * @note the addresses in the ARP table are in network order! + * + * @param netif points to interface index + * @param ipaddr points to the (network order) IP address index + * @param eth_ret points to return pointer + * @param ip_ret points to return pointer + * @return table index if found, -1 otherwise + */ +s8_t +etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret) +{ + s8_t i; + + LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL", + eth_ret != NULL && ip_ret != NULL); + + LWIP_UNUSED_ARG(netif); + + i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); + if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { + *eth_ret = &arp_table[i].ethaddr; + *ip_ret = &arp_table[i].ipaddr; + return i; + } + return -1; +} + +#if ETHARP_TRUST_IP_MAC +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param p The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +static void +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + ip_addr_t iphdr_src; + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { + iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + ip_addr_copy(iphdr_src, iphdr->src); + + /* source is not on the local network? */ + if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update the source IP address in the cache, if present */ + /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY); +} +#endif /* ETHARP_TRUST_IP_MAC */ + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param ethaddr Ethernet address of netif. + * @param p The ARP packet that arrived on netif. Is freed by this function. + * + * @return NULL + * + * @see pbuf_free() + */ +static void +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + struct eth_hdr *ethhdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + ip_addr_t sipaddr, dipaddr; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ERROR("netif != NULL", (netif != NULL), return;); + + /* drop short ARP packets: we have to check for p->len instead of p->tot_len here + since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */ + if (p->len < SIZEOF_ETHARP_PACKET) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, + (s16_t)SIZEOF_ETHARP_PACKET)); + ETHARP_STATS_INC(etharp.lenerr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#if ETHARP_SUPPORT_VLAN + if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) { + hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + /* RFC 826 "Packet Reception": */ + if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || + (hdr->hwlen != ETHARP_HWADDR_LEN) || + (hdr->protolen != sizeof(ip_addr_t)) || + (hdr->proto != PP_HTONS(ETHTYPE_IP))) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, + ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen)); + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + pbuf_free(p); + return; + } + ETHARP_STATS_INC(etharp.recv); + +#if LWIP_AUTOIP + /* We have to check if a host already has configured our random + * created link local address and continously check if there is + * a host with this IP-address so we can detect collisions */ + autoip_arp_reply(netif, hdr); +#endif /* LWIP_AUTOIP */ + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). */ + IPADDR2_COPY(&sipaddr, &hdr->sipaddr); + IPADDR2_COPY(&dipaddr, &hdr->dipaddr); + + /* this interface is not configured? */ + if (ip_addr_isany(&netif->ip_addr)) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? + -> add IP address in ARP cache; assume requester wants to talk to us, + can result in directly sending the queued packets for this host. + ARP message not directed to us? + -> update the source IP address in the cache, if present */ + etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), + for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); + + /* now act on the message itself */ + switch (hdr->opcode) { + /* ARP request? */ + case PP_HTONS(ARP_REQUEST): + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* Re-use pbuf to send ARP reply. + Since we are re-using an existing pbuf, we can't call etharp_raw since + that would allocate a new pbuf. */ + hdr->opcode = htons(ARP_REPLY); + + IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr); + IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr); +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, &hdr->shwaddr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(&hdr->shwaddr, ethaddr); + ETHADDR16_COPY(ðhdr->src, ethaddr); + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* we are not configured? */ + } else if (ip_addr_isany(&netif->ip_addr)) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case PP_HTONS(ARP_REPLY): + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? */ + dhcp_arp_reply(netif, &sipaddr); +#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + ETHARP_STATS_INC(etharp.err); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IP packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param q The pbuf(s) containing the IP packet to be sent. + * @param ipaddr The IP address of the packet destination. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or etharp_send_ip(). + */ +err_t +etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) +{ + struct eth_addr *dest; + struct eth_addr mcastaddr; + ip_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = LL_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) && + !ip_addr_islinklocal(ipaddr)) { +#if LWIP_AUTOIP + struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload + + sizeof(struct eth_hdr)); + /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with + a link-local source address must always be "directly to its destination + on the same physical link. The host MUST NOT send the packet to any + router for forwarding". */ + if (!ip_addr_islinklocal(&iphdr->src)) +#endif /* LWIP_AUTOIP */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + dst_addr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + } +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t etharp_cached_entry = *(netif->addr_hint); + if (etharp_cached_entry < ARP_TABLE_SIZE) { +#endif /* LWIP_NETIF_HWADDRHINT */ + if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + /* send packet directly on the link */ + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @note q must only be ONE packet, not a packet queue! + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t +etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD); + + /* could not find or create entry? */ + if (i < 0) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n")); + ETHARP_STATS_INC(etharp.memerr); + } + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state >= ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + if (result != ERR_OK) { + /* ARP request couldn't be sent */ + /* We don't re-send arp request in etharp_tmr, but we still queue packets, + since this failure could be temporary, and the next packet calling + etharp_query again could lead to sending the queued packets. */ + } + if (q == NULL) { + return result; + } + } + + /* packet given? */ + LWIP_ASSERT("q != NULL", q != NULL); + /* stable entry? */ + if (arp_table[i].state >= ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + ETHARP_SET_HINT(netif, i); + /* send the packet */ + result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr)); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry is still pending, queue the given packet 'q' */ + struct pbuf *p; + int copy_needed = 0; + /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but + * to copy the whole queue into a new PBUF_RAM (see bug #11400) + * PBUF_ROMs can be left as they are, since ROM must not get changed. */ + p = q; + while (p) { + LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0)); + if(p->type != PBUF_ROM) { + copy_needed = 1; + break; + } + p = p->next; + } + if(copy_needed) { + /* copy the whole packet into new pbufs */ + p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(p != NULL) { + if (pbuf_copy(p, q) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } else { + /* referencing the old pbuf is enough */ + p = q; + pbuf_ref(p); + } + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ +#if ARP_QUEUEING + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); + if (new_entry != NULL) { + new_entry->next = 0; + new_entry->p = p; + if(arp_table[i].q != NULL) { + /* queue was already existent, append the new entry to the end */ + struct etharp_q_entry *r; + r = arp_table[i].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + /* the pool MEMP_ARP_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } +#else /* ARP_QUEUEING */ + /* always queue one packet per ARP request only, freeing a previously queued packet */ + if (arp_table[i].q != NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + pbuf_free(arp_table[i].q); + } + arp_table[i].q = p; + result = ERR_OK; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); +#endif /* ARP_QUEUEING */ + } else { + ETHARP_STATS_INC(etharp.memerr); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + result = ERR_MEM; + } + } + return result; +} + +/** + * Send a raw ARP packet (opcode and all addresses can be modified) + * + * @param netif the lwip network interface on which to send the ARP packet + * @param ethsrc_addr the source MAC address for the ethernet header + * @param ethdst_addr the destination MAC address for the ethernet header + * @param hwsrc_addr the source MAC address for the ARP protocol header + * @param ipsrc_addr the source IP address for the ARP protocol header + * @param hwdst_addr the destination MAC address for the ARP protocol header + * @param ipdst_addr the destination IP address for the ARP protocol header + * @param opcode the type of the ARP packet + * @return ERR_OK if the ARP packet has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +#if !LWIP_AUTOIP +static +#endif /* LWIP_AUTOIP */ +err_t +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr, + const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + struct eth_hdr *ethhdr; + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p == NULL) { + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("etharp_raw: could not allocate pbuf for ARP request.\n")); + ETHARP_STATS_INC(etharp.memerr); + return ERR_MEM; + } + LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr", + (p->len >= SIZEOF_ETHARP_PACKET)); + + ethhdr = (struct eth_hdr *)p->payload; + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n")); + hdr->opcode = htons(opcode); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if LWIP_AUTOIP + /* If we are using Link-Local, all ARP packets that contain a Link-Local + * 'sender IP address' MUST be sent using link-layer broadcast instead of + * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */ + ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write the ARP MAC-Addresses */ + ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); + ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ETHADDR16_COPY(ðhdr->dest, ethdst_hwaddr); +#else /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->dest, ethdst_addr); +#endif /* LWIP_AUTOIP */ + ETHADDR16_COPY(ðhdr->src, ethsrc_addr); + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing. */ + IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); + IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); + + hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); + hdr->proto = PP_HTONS(ETHTYPE_IP); + /* set hwlen and protolen */ + hdr->hwlen = ETHARP_HWADDR_LEN; + hdr->protolen = sizeof(ip_addr_t); + + ethhdr->type = PP_HTONS(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + ETHARP_STATS_INC(etharp.xmit); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + + return result; +} + +/** + * Send an ARP request packet asking for ipaddr. + * + * @param netif the lwip network interface on which to send the request + * @param ipaddr the IP address for which to ask + * @return ERR_OK if the request has been sent + * ERR_MEM if the ARP packet couldn't be allocated + * any other err_t on failure + */ +err_t +etharp_request(struct netif *netif, ip_addr_t *ipaddr) +{ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n")); + return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, + (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, + ipaddr, ARP_REQUEST); +} +#endif /* LWIP_ARP */ + +/** + * Process received ethernet frames. Using this function instead of directly + * calling ip_input and passing ARP frames through etharp in ethernetif_input, + * the ARP cache is protected from concurrent access. + * + * @param p the recevied packet, p->payload pointing to the ethernet header + * @param netif the network interface on which the packet was received + */ +err_t +ethernet_input(struct pbuf *p, struct netif *netif) +{ + struct eth_hdr* ethhdr; + u16_t type; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = (struct eth_hdr *)p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", + (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2], + (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5], + (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2], + (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5], + (unsigned)htons(ethhdr->type))); + + type = ethhdr->type; +#if ETHARP_SUPPORT_VLAN + if (type == PP_HTONS(ETHTYPE_VLAN)) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); + if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { + /* a packet with only an ethernet/vlan header (or less) is not valid for us */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef ETHARP_VLAN_CHECK_FN + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ + type = vlan->tpid; + ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; + } +#endif /* ETHARP_SUPPORT_VLAN */ + +#if LWIP_ARP_FILTER_NETIF + netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type)); +#endif /* LWIP_ARP_FILTER_NETIF*/ + + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { + if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } + } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + switch (type) { +#if LWIP_ARP + /* IP packet? */ + case PP_HTONS(ETHTYPE_IP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } +#if ETHARP_TRUST_IP_MAC + /* update ARP table */ + etharp_ip_input(netif, p); +#endif /* ETHARP_TRUST_IP_MAC */ + /* skip Ethernet header */ + if(pbuf_header(p, -ip_hdr_offset)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case PP_HTONS(ETHTYPE_ARP): + if (!(netif->flags & NETIF_FLAG_ETHARP)) { + goto free_and_return; + } + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; +#endif /* LWIP_ARP */ +#if PPPOE_SUPPORT + case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ + pppoe_data_input(netif, p); + break; +#endif /* PPPOE_SUPPORT */ + +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IPv6 layer */ + ip6_input(p, netif); + } + break; +#endif /* LWIP_IPV6 */ + + default: + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; + +free_and_return: + pbuf_free(p); + return ERR_OK; +} +#endif /* LWIP_ARP || LWIP_ETHERNET */ diff --git a/external/badvpn_dns/lwip/src/netif/ethernetif.c b/external/badvpn_dns/lwip/src/netif/ethernetif.c new file mode 100644 index 00000000..46900bdb --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ethernetif.c @@ -0,0 +1,322 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_IPV6: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/auth.c b/external/badvpn_dns/lwip/src/netif/ppp/auth.c new file mode 100644 index 00000000..0fd87a37 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/auth.c @@ -0,0 +1,1334 @@ +/***************************************************************************** +* auth.c - Network Authentication and Phase Control program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Ported from public pppd code. +*****************************************************************************/ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "lcp.h" +#include "pap.h" +#include "chap.h" +#include "auth.h" +#include "ipcp.h" + +#if CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + +#include "lwip/inet.h" + +#include + +#if 0 /* UNUSED */ +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) +#endif /* UNUSED */ + +#if PAP_SUPPORT || CHAP_SUPPORT +/* The name by which the peer authenticated itself to us. */ +static char peer_authname[MAXNAMELEN]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called plogin() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; /* @todo, we don't need this in lwip*/ + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +#if 0 /* UNUSED */ +/* Wordlist giving addresses which the peer may use + without authenticating itself. */ +static struct wordlist *noauth_addrs; + +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; +#endif /* UNUSED */ + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +#if PAP_SUPPORT || CHAP_SUPPORT +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* Set if we require authentication only because we have a default route. */ +static bool default_auth; + +/* Hook to enable a plugin to control the idle time limit */ +int (*idle_time_hook) __P((struct ppp_idle *)) = NULL; + +/* Hook for a plugin to say whether we can possibly authenticate any peer */ +int (*pap_check_hook) __P((void)) = NULL; + +/* Hook for a plugin to check the PAP user and password */ +int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, + struct wordlist **paddrs, + struct wordlist **popts)) = NULL; + +/* Hook for a plugin to know about the PAP user logout */ +void (*pap_logout_hook) __P((void)) = NULL; + +/* Hook for a plugin to get the PAP password for authenticating us */ +int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL; + +/* + * This is used to ensure that we don't start an auth-up/down + * script while one is already running. + */ +enum script_state { + s_down, + s_up +}; + +static enum script_state auth_state = s_down; +static enum script_state auth_script_state = s_down; +static pid_t auth_script_pid = 0; + +/* + * Option variables. + * lwip: some of these are present in the ppp_settings structure + */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ +bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +bool usehostname = 0; /* Use hostname for our_name */ +bool auth_required = 0; /* Always require authentication from peer */ +bool allow_any_ip = 0; /* Allow peer to use any IP address */ +bool explicit_remote = 0; /* User specified explicit remote name */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ + +#endif /* UNUSED */ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +/* @todo, move this somewhere */ +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + + +extern char *crypt (const char *, const char *); + +/* Prototypes for procedures local to this file. */ + +static void network_phase (int); +static void check_idle (void *); +static void connect_time_expired (void *); +#if 0 +static int plogin (char *, char *, char **, int *); +#endif +static void plogout (void); +static int null_login (int); +static int get_pap_passwd (int, char *, char *); +static int have_pap_secret (void); +static int have_chap_secret (char *, char *, u32_t); +static int ip_addr_check (u32_t, struct wordlist *); + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +static int scan_authfile (FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *); +static void free_wordlist (struct wordlist *); +static void auth_script (char *); +static void auth_script_done (void *); +static void set_allowed_addrs (int unit, struct wordlist *addrs); +static int some_ip_ok (struct wordlist *); +static int setupapfile (char **); +static int privgroup (char **); +static int set_noauth_addr (char **); +static void check_access (FILE *, char *); +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * Authentication-related options. + */ +option_t auth_options[] = { + { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "refuse-pap", o_bool, &refuse_pap, + "Don't agree to auth to peer with PAP", 1 }, + { "-pap", o_bool, &refuse_pap, + "Don't allow PAP authentication with peer", 1 }, + { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "+chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "refuse-chap", o_bool, &refuse_chap, + "Don't agree to auth to peer with CHAP", 1 }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", 1 }, + { "name", o_string, our_name, + "Set local name for authentication", + OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN }, + { "user", o_string, user, + "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN }, + { "usehostname", o_bool, &usehostname, + "Must use hostname for authentication", 1 }, + { "remotename", o_string, remote_name, + "Set remote name for authentication", OPT_STATIC, + &explicit_remote, MAXNAMELEN }, + { "auth", o_bool, &auth_required, + "Require authentication from peer", 1 }, + { "noauth", o_bool, &auth_required, + "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip }, + { "login", o_bool, &uselogin, + "Use system password database for PAP", 1 }, + { "papcrypt", o_bool, &cryptpap, + "PAP passwords are encrypted", 1 }, + { "+ua", o_special, (void *)setupapfile, + "Get PAP user and password from file" }, + { "password", o_string, passwd, + "Password for authenticating us to the peer", OPT_STATIC, + NULL, MAXSECRETLEN }, + { "privgroup", o_special, (void *)privgroup, + "Allow group members to use privileged options", OPT_PRIV }, + { "allow-ip", o_special, (void *)set_noauth_addr, + "Set IP address(es) which can be used without authentication", + OPT_PRIV }, + { NULL } +}; +#endif /* UNUSED */ +#if 0 /* UNUSED */ +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(char **argv) +{ + FILE * ufile; + int l; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + seteuid(getuid()); + ufile = fopen(*argv, "r"); + seteuid(0); + if (ufile == NULL) { + option_error("unable to open user login data file %s", *argv); + return 0; + } + check_access(ufile, *argv); + + /* get username */ + if (fgets(user, MAXNAMELEN - 1, ufile) == NULL + || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){ + option_error("unable to read user login data file %s", *argv); + return 0; + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(user); + if (l > 0 && user[l-1] == '\n') + user[l-1] = 0; + l = strlen(passwd); + if (l > 0 && passwd[l-1] == '\n') + passwd[l-1] = 0; + + return (1); +} +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* + * privgroup - allow members of the group to have privileged access. + */ +static int +privgroup(char **argv) +{ + struct group *g; + int i; + + g = getgrnam(*argv); + if (g == 0) { + option_error("group %s is unknown", *argv); + return 0; + } + for (i = 0; i < ngroups; ++i) { + if (groups[i] == g->gr_gid) { + privileged = 1; + break; + } + } + return 1; +} +#endif + +#if 0 /* UNUSED */ +/* + * set_noauth_addr - set address(es) that can be used without authentication. + * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. + */ +static int +set_noauth_addr(char **argv) +{ + char *addr = *argv; + int l = strlen(addr); + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1); + if (wp == NULL) + novm("allow-ip argument"); + wp->word = (char *) (wp + 1); + wp->next = noauth_addrs; + BCOPY(addr, wp->word, l); + noauth_addrs = wp; + return 1; +} +#endif /* UNUSED */ + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(int unit) +{ + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit)); +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(int unit) +{ + AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + plogout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n")); + pppLinkTerminated(unit); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(int unit) +{ + int i; + struct protent *protp; + + AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit)); + + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; + } + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); + } + } + num_np_open = 0; /* number of network protocols we have opened */ + num_np_up = 0; /* Number of network protocols which have come up */ + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + pppLinkDown(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(int unit) +{ + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n")); + } + } + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(int unit) +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } + } + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } +} +/* @todo: add void start_networks(void) here (pppd 2.3.11) */ + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(int unit, u16_t protocol) +{ + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + + +#if PAP_SUPPORT || CHAP_SUPPORT +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_PEER; + break; + case PPP_PAP: + pbit = PAP_PEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > (int)sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(int unit, u16_t protocol) +{ + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ + pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + lcp_close(unit, "Failed to authenticate ourselves to peer"); +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(int unit, u16_t protocol) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); + } + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(void *arg) +{ + struct ppp_idle idle; + u_short itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + AUTHDEBUG(LOG_INFO, ("Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +#if 0 /* UNUSED */ +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options(void) +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } +} +#endif /* UNUSED */ + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(int unit) +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; + } + } +} + +#if PAP_SUPPORT +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +u_char +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) +{ +#if 1 /* XXX Assume all entries OK. */ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ +#else + u_char ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u_short attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ + + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + /* @todo: this was sleep(), i.e. seconds, not milliseconds + * I don't think we really need this in lwIP - we would block tcpip_thread! + */ + /*sys_msleep((attempts - 3) * 5);*/ + } + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +#endif +} +#endif /* PAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * This function is needed for PAM. + */ + +#ifdef USE_PAM + +/* lwip does not support PAM*/ + +#endif /* USE_PAM */ + +#endif /* UNUSED */ + + +#if 0 /* UNUSED */ +/* + * plogin - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +plogin(char *user, char *passwd, char **msg, int *msglen) +{ + + LWIP_UNUSED_ARG(user); + LWIP_UNUSED_ARG(passwd); + LWIP_UNUSED_ARG(msg); + LWIP_UNUSED_ARG(msglen); + + + /* The new lines are here align the file when + * compared against the pppd 2.3.11 code */ + + + + + + + + + + + + + + + + + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); +} +#endif + + + +/* + * plogout - Logout the user. + */ +static void +plogout(void) +{ + logged_in = 0; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; +} + + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_pap_passwd(int unit, char *user, char *passwd) +{ + LWIP_UNUSED_ARG(unit); +/* normally we would reject PAP if no password is provided, + but this causes problems with some providers (like CHT in Taiwan) + who incorrectly request PAP and expect a bogus/empty password, so + always provide a default user/passwd of "none"/"none" + + @todo: This should be configured by the user, instead of being hardcoded here! +*/ + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(void) +{ + /* XXX Fail until we set up our passwords. */ + return 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(char *client, char *server, u32_t remote) +{ + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + + /* XXX Fail until we set up our passwords. */ + return 0; +} +#if CHAP_SUPPORT + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) +{ +#if 1 + int len; + struct wordlist *addrs; + + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; + + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = (int)strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; +#else + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } + + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} +#endif /* CHAP_SUPPORT */ + + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(int unit, struct wordlist *addrs) +{ + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; + +#if 0 + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } + } + } +#endif +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(int unit, u32_t addr) +{ + return ip_addr_check(addr, addresses[unit]); +} + +static int /* @todo: integrate this funtion into auth_ip_addr()*/ +ip_addr_check(u32_t addr, struct wordlist *addrs) +{ + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(u32_t addr) +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * some_ip_ok - check a wordlist to see if it authorizes any + * IP address(es). + */ +static int +some_ip_ok(struct wordlist *addrs) +{ + for (; addrs != 0; addrs = addrs->next) { + if (addrs->word[0] == '-') + break; + if (addrs->word[0] != '!') + return 1; /* some IP address is allowed */ + } + return 0; +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +static void +check_access(FILE *f, char *filename) +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + warn("cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + warn("Warning - secret file %s has world and/or group access", + filename); + } +} + + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line up to a "--" (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. Any + * following words (extra options) are placed in a wordlist and + * returned in *opts. + * We assume secret is NULL or points to MAXWORDLEN bytes of space. + */ +static int +scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename) +{ + /* We do not (currently) need this in lwip */ + return 0; /* dummy */ +} +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(struct wordlist *wp) +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} + +/* + * auth_script_done - called when the auth-up or auth-down script + * has finished. + */ +static void +auth_script_done(void *arg) +{ + auth_script_pid = 0; + switch (auth_script_state) { + case s_up: + if (auth_state == s_down) { + auth_script_state = s_down; + auth_script(_PATH_AUTHDOWN); + } + break; + case s_down: + if (auth_state == s_up) { + auth_script_state = s_up; + auth_script(_PATH_AUTHUP); + } + break; + } +} + +/* + * auth_script - execute a script with arguments + * interface-name peer-name real-user tty speed + */ +static void +auth_script(char *script) +{ + char strspeed[32]; + struct passwd *pw; + char struid[32]; + char *user_name; + char *argv[8]; + + if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL) + user_name = pw->pw_name; + else { + slprintf(struid, sizeof(struid), "%d", getuid()); + user_name = struid; + } + slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); + + argv[0] = script; + argv[1] = ifname; + argv[2] = peer_authname; + argv[3] = user_name; + argv[4] = devnam; + argv[5] = strspeed; + argv[6] = NULL; + + auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL); +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/auth.h b/external/badvpn_dns/lwip/src/netif/ppp/auth.h new file mode 100644 index 00000000..a8069ec4 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/auth.h @@ -0,0 +1,111 @@ +/***************************************************************************** +* auth.h - PPP Authentication and phase control header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD pppd.h. +*****************************************************************************/ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); + +/* peer successfully authenticated itself */ +void auth_peer_success (int, u16_t, char *, int); + +/* we failed to authenticate ourselves */ +void auth_withpeer_fail (int, u16_t); + +/* we successfully authenticated ourselves */ +void auth_withpeer_success (int, u16_t); + +/* check authentication options supplied */ +void auth_check_options (void); + +/* check what secrets we have */ +void auth_reset (int); + +/* Check peer-supplied username/password */ +u_char check_passwd (int, char *, int, char *, int, char **, int *); + +/* get "secret" for chap */ +int get_secret (int, char *, char *, char *, int *, int); + +/* check if IP address is authorized */ +int auth_ip_addr (int, u32_t); + +/* check if IP address is unreasonable */ +int bad_ip_adrs (u32_t); + +#endif /* AUTH_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/chap.c b/external/badvpn_dns/lwip/src/netif/ppp/chap.c new file mode 100644 index 00000000..f10e27d2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/chap.c @@ -0,0 +1,908 @@ +/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/ +/***************************************************************************** +* chap.c - Network Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap.c. +*****************************************************************************/ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "magic.h" +#include "randm.h" +#include "auth.h" +#include "md5.h" +#include "chap.h" +#include "chpms.h" + +#include + +#if 0 /* UNUSED */ +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap[0].timeouttime, + "Set timeout for CHAP" }, + { "chap-max-challenge", o_int, &chap[0].max_transmits, + "Set max #xmits for challenge" }, + { "chap-interval", o_int, &chap[0].chal_interval, + "Set interval for rechallenge" }, +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif + { NULL } +}; +#endif /* UNUSED */ + +/* + * Protocol entry points. + */ +static void ChapInit (int); +static void ChapLowerUp (int); +static void ChapLowerDown (int); +static void ChapInput (int, u_char *, int); +static void ChapProtocolReject (int); +#if PPP_ADDITIONAL_CALLBACKS +static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + ChapPrintPkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "CHAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout (void *); +static void ChapResponseTimeout (void *); +static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int); +static void ChapRechallenge (void *); +static void ChapReceiveResponse (chap_state *, u_char *, int, int); +static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapSendStatus (chap_state *, int); +static void ChapSendChallenge (chap_state *); +static void ChapSendResponse (chap_state *); +static void ChapGenChallenge (chap_state *); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(int unit) +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(int unit) +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(ChapChallengeTimeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(ChapRechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ + } + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(int unit, u_char *inpacket, int packet_len) +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code)); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len) +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n", + rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n", + rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(chap_state *cstate) +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = (int)strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(chap_state *cstate, int code) +{ + u_char *outp; + int outlen, msglen; + char msg[256]; /* @todo: this can be a char*, no strcpy needed */ + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = (int)strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(chap_state *cstate) +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff); + cstate->chal_len = (u_char)chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic() & 0xff); + } +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(chap_state *cstate) +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = (int)strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static int +ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) { + printer(arg, " %s", ChapCodenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/chap.h b/external/badvpn_dns/lwip/src/netif/ppp/chap.h new file mode 100644 index 00000000..fedcab8d --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/chap.h @@ -0,0 +1,150 @@ +/***************************************************************************** +* chap.h - Network Challenge Handshake Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-03 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $ + */ + +#ifndef CHAP_H +#define CHAP_H + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +extern chap_state chap[]; + +void ChapAuthWithPeer (int, char *, u_char); +void ChapAuthPeer (int, char *, u_char); + +extern struct protent chap_protent; + +#endif /* CHAP_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/chpms.c b/external/badvpn_dns/lwip/src/netif/ppp/chpms.c new file mode 100644 index 00000000..81a887b8 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/chpms.c @@ -0,0 +1,396 @@ +/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/ +/*** The original PPPD code is written in a way to require either the UNIX DES + encryption functions encrypt(3) and setkey(3) or the DES library libdes. + Since both is not included in lwIP, MSCHAP currently does not work! */ +/***************************************************************************** +* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap_ms.c. +*****************************************************************************/ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#define USE_CRYPT + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md4.h" +#ifndef USE_CRYPT +#include "des.h" +#endif +#include "chap.h" +#include "chpms.h" + +#include + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ + +/* XXX Don't know what to do with these. */ +extern void setkey(const char *); +extern void encrypt(char *, int); + +static void DesEncrypt (u_char *, u_char *, u_char *); +static void MakeKey (u_char *, u_char *); + +#ifdef USE_CRYPT +static void Expand (u_char *, u_char *); +static void Collapse (u_char *, u_char *); +#endif + +static void ChallengeResponse( + u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */ +); +static void ChapMS_NT( + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response +); +static u_char Get7Bits( + u_char *input, + int startBit +); + +static void +ChallengeResponse( u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */) +{ + u_char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey((char*)crypt_key); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt((char*)des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char +Get7Bits( u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void +Expand(u_char *in, u_char *out) +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } +} + +/* The inverse of Expand + */ +static void +Collapse(u_char *in, u_char *out) +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } +} +#endif + +static void +MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */ + u_char *des_key /* OUT 64 bit DES key with parity bits added */) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + MDstruct md4Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + LWIP_UNUSED_ARG(rchallenge_len); + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u_char)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (PP_HTONS((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + /* @todo: arg type - u_long* or u_int* ? */ + MDreverse((unsigned int*)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u_char)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* MSCHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/chpms.h b/external/badvpn_dns/lwip/src/netif/ppp/chpms.h new file mode 100644 index 00000000..df070fb3 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/chpms.h @@ -0,0 +1,64 @@ +/***************************************************************************** +* chpms.h - Network Microsoft Challenge Handshake Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-01-30 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. The name of the author may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef CHPMS_H +#define CHPMS_H + +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS (chap_state *, char *, int, char *, int); + +#endif /* CHPMS_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/fsm.c b/external/badvpn_dns/lwip/src/netif/ppp/fsm.c new file mode 100644 index 00000000..e8a254ed --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/fsm.c @@ -0,0 +1,890 @@ +/***************************************************************************** +* fsm.c - Network Control Protocol Finite State Machine program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD fsm.c. +*****************************************************************************/ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" + +#include + +#if PPP_DEBUG +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; +#endif /* PPP_DEBUG */ + +static void fsm_timeout (void *); +static void fsm_rconfreq (fsm *, u_char, u_char *, int); +static void fsm_rconfack (fsm *, int, u_char *, int); +static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); +static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rtermack (fsm *); +static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_sconfreq (fsm *, int); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +int peer_mru[NUM_PPP]; + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(fsm *f) +{ + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; + + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + +#if 0 /* backport pppd 2.4.4b1; */ +/* + * terminate_layer - Start process of shutting down the FSM + * + * Cancel any timeout running, notify upper layers we're done, and + * send a terminate-request message as configured. + */ +static void +terminate_layer(fsm *f, int nextstate) +{ + /* @todo */ +} +#endif + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the LS_CLOSED state. + */ +void +fsm_close(fsm *f, char *reason) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(void *arg) +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(fsm *f, u_char *inpacket, int l) +{ + u_char *inp = inpacket; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f))); + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +{ + int code, reject_if_disagree; + + FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u_char)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(fsm *f, int id, u_char *inp, int len) +{ + FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +{ + int (*proc) (fsm *, u_char *, int); + int ret; + + FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(fsm *f, int id, u_char *p, int len) +{ + LWIP_UNUSED_ARG(p); + + FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(fsm *f) +{ + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + default: + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(fsm *f, u_char *inp, int len) +{ + u_char code, id; + + FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(fsm *f) +{ + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(fsm *f, int retransmit) +{ + u_char *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); +} + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/fsm.h b/external/badvpn_dns/lwip/src/netif/ppp/fsm.h new file mode 100644 index 00000000..8d41b5f5 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/fsm.h @@ -0,0 +1,157 @@ +/***************************************************************************** +* fsm.h - Network Control Protocol Finite State Machine header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD code. +*****************************************************************************/ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $ + */ + +#ifndef FSM_H +#define FSM_H + +/* + * LCP Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Prototypes + */ +void fsm_init (fsm*); +void fsm_lowerup (fsm*); +void fsm_lowerdown (fsm*); +void fsm_open (fsm*); +void fsm_close (fsm*, char*); +void fsm_input (fsm*, u_char*, int); +void fsm_protreject (fsm*); +void fsm_sdata (fsm*, u_char, u_char, u_char*, int); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ + +#endif /* FSM_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ipcp.c b/external/badvpn_dns/lwip/src/netif/ppp/ipcp.c new file mode 100644 index 00000000..f0ab2e0e --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ipcp.c @@ -0,0 +1,1411 @@ +/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and + dial-on-demand has been stripped. */ +/***************************************************************************** +* ipcp.c - Network PPP IP Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "fsm.h" +#include "vj.h" +#include "ipcp.h" + +#include "lwip/inet.h" + +#include + +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ + +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +/* local vars */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ + + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ +#if PPP_ADDITIONAL_CALLBACKS +static void ipcp_script (fsm *, char *); /* Run an up/down script */ +#endif +static void ipcp_finished (fsm *); /* Don't need lower layer */ + + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Protocol entry points from main code. + */ +static void ipcp_init (int); +static void ipcp_open (int); +static void ipcp_close (int, char *); +static void ipcp_lowerup (int); +static void ipcp_lowerdown (int); +static void ipcp_input (int, u_char *, int); +static void ipcp_protrej (int); + + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if PPP_ADDITIONAL_CALLBACKS + ipcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "IPCP", +#if PPP_ADDITIONAL_CALLBACKS + ip_check_options, + NULL, + ip_active_pkt +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +static void ipcp_clear_addrs (int); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(int unit) +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(int unit) +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(int unit, char *reason) +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(int unit) +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(int unit, u_char *p, int len) +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(fsm *f) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(fsm *f) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG(LOG_INFO, ("remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; +#ifdef OLD_CI_ADDRS + ipcp_options *go = &ipcp_gotoptions[f->unit]; +#endif + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ +#ifdef OLD_CI_ADDRS + u32_t ciaddr2; /* Parsed address values */ +#endif + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = (u_short)l;/* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +#ifdef OLD_CI_ADDRS /* Need to save space... */ + case CI_ADDRS: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; +#endif + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG(LOG_INFO, ( + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options(u_long localAddr) +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } +} +#endif + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(fsm *f) +{ + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG(LOG_INFO, ("ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG(LOG_WARNING, ("sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(fsm *f) +{ + IPCPDEBUG(LOG_INFO, ("ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, etc. + */ +static void +ipcp_clear_addrs(int unit) +{ + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(fsm *f) +{ + np_finished(f->unit, PPP_IP); +} + +#if PPP_ADDITIONAL_CALLBACKS +static int +ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(u_char *pkt, int len) +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ipcp.h b/external/badvpn_dns/lwip/src/netif/ppp/ipcp.h new file mode 100644 index 00000000..de03f460 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ipcp.h @@ -0,0 +1,106 @@ +/***************************************************************************** +* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef IPCP_H +#define IPCP_H + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ + +typedef struct ipcp_options { + u_int neg_addr : 1; /* Negotiate IP Address? */ + u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ + u_int req_addr : 1; /* Ask peer to send IP address? */ + u_int default_route : 1; /* Assign default route through interface? */ + u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u_int neg_vj : 1; /* Van Jacobson Compression? */ + u_int old_vj : 1; /* use old (short) form of VJ option? */ + u_int accept_local : 1; /* accept peer's value for ouraddr */ + u_int accept_remote : 1; /* accept peer's value for hisaddr */ + u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex; /* VJ slots - 1. */ + u_char cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +extern struct protent ipcp_protent; + +#endif /* IPCP_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/lcp.c b/external/badvpn_dns/lwip/src/netif/ppp/lcp.c new file mode 100644 index 00000000..54f758aa --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/lcp.c @@ -0,0 +1,2066 @@ +/***************************************************************************** +* lcp.c - Network Link Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "chap.h" +#include "magic.h" +#include "auth.h" +#include "lcp.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + +#if 0 /* UNUSED */ +/* + * LCP-related command-line options. + */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +bool lax_recv = 0; /* accept control chars in asyncmap */ + +static int setescape (char **); + +static option_t lcp_option_list[] = { + /* LCP options */ + /* list stripped for simplicity */ + {NULL} +}; +#endif /* UNUSED */ + +/* options */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ + +/* global vars */ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ + +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */ +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u_char, u_char*, int); +static void lcp_rprotrej (fsm*, u_char*, int); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup (int); +static void lcp_echo_lowerdown (int); +static void LcpEchoTimeout (void*); +static void lcp_received_echo_reply (fsm*, int, u_char*, int); +static void LcpSendEchoRequest (fsm*); +static void LcpLinkFailure (fsm*); +static void LcpEchoCheck (fsm*); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_input (int, u_char *, int); +static void lcp_protrej (int); + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if PPP_ADDITIONAL_CALLBACKS + lcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "LCP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED */ +/* + * setescape - add chars to the set we escape on transmission. + */ +static int +setescape(argv) + char **argv; +{ + int n, ret; + char *p, *endp; + + p = *argv; + ret = 1; + while (*p) { + n = strtol(p, &endp, 16); + if (p == endp) { + option_error("escape parameter contains invalid hex number '%s'", p); + return 0; + } + p = endp; + if (n < 0 || n == 0x5E || n > 0xFF) { + option_error("can't escape character 0x%x", n); + ret = 0; + } else + xmit_accm[0][n >> 5] |= 1 << (n & 0x1F); + while (*p == ',' || *p == ' ') + ++p; + } + return ret; +} +#endif /* UNUSED */ + +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(int unit, char *reason) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(f, reason); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(int unit) +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0] + | ((u_long)xmit_accm[unit][1] << 8) + | ((u_long)xmit_accm[unit][2] << 16) + | ((u_long)xmit_accm[unit][3] << 24); + LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(int unit) +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(int unit, u_char *p, int len) +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(fsm *f, u_char *inp, int len) +{ + int i; + struct protent *protp; + u_short prot; + + if (len < (int)sizeof (u_short)) { + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot)); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +static void +lcp_protrej(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(int unit, u_char *p, int len) +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(fsm *f) +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n")); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n")); + return (1); +bad: + LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(fsm *f, + u_char *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype; /* Parsed len, type */ + u_char cichar; /* Parsed char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ +#if TRACELCP > 0 + char traceBuf[80]; + size_t traceNdx = 0; +#endif + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#if MSCHAP_SUPPORT + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); +#endif + + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); +#endif + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + default: +#if TRACELCP + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } +#endif + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + +#if TRACELCP > 0 + if (traceNdx > 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf)); + } +#endif + LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(fsm *f) +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); /* The link is up; authenticate now */ +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(fsm *f) +{ + link_required(f->unit); /* lwip: currently does nothing */ +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(fsm *f) +{ + link_terminated(f->unit); /* we are finished with the link */ +} + + +#if PPP_ADDITIONAL_CALLBACKS +/* + * print_string - print a readable representation of a string using + * printer. + */ +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } + } + printer(arg, "\""); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +/* + * Time to shut down the link because there is nothing out there. + */ +static void +LcpLinkFailure (fsm *f) +{ + if (f->state == LS_OPENED) { + LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ +static void +LcpEchoCheck (fsm *f) +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ +static void +LcpEchoTimeout (void *arg) +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ +static void +lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +{ + u32_t magic; + + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ +static void +LcpSendEchoRequest (fsm *f) +{ + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + ++lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + LcpEchoCheck (f); + } +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/lcp.h b/external/badvpn_dns/lwip/src/netif/ppp/lcp.h new file mode 100644 index 00000000..b9201eeb --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/lcp.h @@ -0,0 +1,151 @@ +/***************************************************************************** +* lcp.h - Network Link Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef LCP_H +#define LCP_H +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ + +/* + * LCP-specific packet types (code numbers). + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + u_int passive : 1; /* Don't die if we don't get a response */ + u_int silent : 1; /* Wait for the other end to start first */ + u_int restart : 1; /* Restart vs. exit after close */ + u_int neg_mru : 1; /* Negotiate the MRU? */ + u_int neg_asyncmap : 1; /* Negotiate the async map? */ + u_int neg_upap : 1; /* Ask for UPAP authentication? */ + u_int neg_chap : 1; /* Ask for CHAP authentication? */ + u_int neg_magicnumber : 1; /* Ask for magic number? */ + u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_int neg_cbcp : 1; /* Negotiate use of CBCP */ +#ifdef PPP_MULTILINK + u_int neg_mrru : 1; /* Negotiate multilink MRRU */ + u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ +#endif + u_short mru; /* Value of MRU */ +#ifdef PPP_MULTILINK + u_short mrru; /* Value of MRRU, and multilink enable */ +#endif + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#ifdef PPP_MULTILINK + struct epdisc endpoint; /* endpoint discriminator */ +#endif +} lcp_options; + +/* + * Values for phase from BSD pppd.h based on RFC 1661. + */ +typedef enum { + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE +} LinkPhase; + + + +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern ext_accm xmit_accm[]; + + +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 + +#endif /* LCP_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/magic.c b/external/badvpn_dns/lwip/src/netif/ppp/magic.c new file mode 100644 index 00000000..3732a424 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/magic.c @@ -0,0 +1,80 @@ +/***************************************************************************** +* magic.c - Network Random Number Generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD magic.c. +*****************************************************************************/ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "ppp_impl.h" +#include "randm.h" +#include "magic.h" + + +/* + * magicInit - Initialize the magic number generator. + * + * Since we use another random number generator that has its own + * initialization, we do nothing here. + */ +void magicInit() +{ + return; +} + +/* + * magic - Returns the next magic number. + */ +u32_t magic() +{ + return avRandom(); +} + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/magic.h b/external/badvpn_dns/lwip/src/netif/ppp/magic.h new file mode 100644 index 00000000..eba70d20 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/magic.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* magic.h - Network Random Number Generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef MAGIC_H +#define MAGIC_H + +/* Initialize the magic number generator */ +void magicInit(void); + +/* Returns the next magic number */ +u32_t magic(void); + +#endif /* MAGIC_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/md5.c b/external/badvpn_dns/lwip/src/netif/ppp/md5.c new file mode 100644 index 00000000..dc3cc751 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/md5.c @@ -0,0 +1,320 @@ +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md5.h" + +#include + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (u32_t *buf, u32_t *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##UL +#else +#ifdef WIN32 +#define UL(x) x##UL +#else +#define UL(x) x +#endif +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (u32_t)0; + + /* Load magic initialization constants. */ + mdContext->buf[0] = (u32_t)0x67452301UL; + mdContext->buf[1] = (u32_t)0xefcdab89UL; + mdContext->buf[2] = (u32_t)0x98badcfeUL; + mdContext->buf[3] = (u32_t)0x10325476UL; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + +#if 0 + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf)); + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf)); +#endif + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) { + mdContext->i[1]++; + } + mdContext->i[0] += ((u32_t)inLen << 3); + mdContext->i[1] += ((u32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +MD5Final (unsigned char hash[], MD5_CTX *mdContext) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + SMEMCPY(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void +Transform (u32_t *buf, u32_t *in) +{ + u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/md5.h b/external/badvpn_dns/lwip/src/netif/ppp/md5.h new file mode 100644 index 00000000..e129533f --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/md5.h @@ -0,0 +1,55 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init ( MD5_CTX *mdContext); +void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final ( unsigned char hash[], MD5_CTX *mdContext); + +#endif /* MD5_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/pap.c b/external/badvpn_dns/lwip/src/netif/ppp/pap.c new file mode 100644 index 00000000..5fb9f886 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/pap.c @@ -0,0 +1,628 @@ +/***************************************************************************** +* pap.c - Network Password Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-12 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "pap.h" + +#include + +#if 0 /* UNUSED */ +static bool hide_password = 1; + +/* + * Command-line options. + */ +static option_t pap_option_list[] = { + { "hide-password", o_bool, &hide_password, + "Don't output passwords to log", 1 }, + { "show-password", o_bool, &hide_password, + "Show password string in debug log messages", 0 }, + { "pap-restart", o_int, &upap[0].us_timeouttime, + "Set retransmit timeout for PAP" }, + { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, + "Set max number of transmissions for auth-reqs" }, + { "pap-timeout", o_int, &upap[0].us_reqtimeout, + "Set time limit for peer PAP authentication" }, + { NULL } +}; +#endif + +/* + * Protocol entry points. + */ +static void upap_init (int); +static void upap_lowerup (int); +static void upap_lowerdown (int); +static void upap_input (int, u_char *, int); +static void upap_protrej (int); +#if PPP_ADDITIONAL_CALLBACKS +static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + upap_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "PAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u_char *, u_char, int); +static void upap_rauthack (upap_state *, u_char *, int, int); +static void upap_rauthnak (upap_state *, u_char *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u_char, u_char, char *, int); + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(int unit, char *user, char *password) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = (int)strlen(user); + u->us_passwd = password; + u->us_passwdlen = (int)strlen(password); + + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(int unit) +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + /* now client state is UPAPCS__AUTHREQ */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(int unit) +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(int unit, u_char *inpacket, int l) +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len)); + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len) +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + u_char retcode; + char *msg; + int msglen; + + UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + /* lwip: currently retcode is always UPAP_AUTHACK */ + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n")); + return; + } + + /* + * Parse message. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nak. + */ +static void +upap_rauthnak(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if(msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(upap_state *u) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static int upap_printpkt( + u_char *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg +) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/pap.h b/external/badvpn_dns/lwip/src/netif/ppp/pap.h new file mode 100644 index 00000000..c99a2040 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/pap.h @@ -0,0 +1,118 @@ +/***************************************************************************** +* pap.h - PPP Password Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef PAP_H +#define PAP_H + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +extern upap_state upap[]; + +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); + +extern struct protent pap_protent; + +#endif /* PAP_SUPPORT */ + +#endif /* PAP_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ppp.c b/external/badvpn_dns/lwip/src/netif/ppp/ppp.c new file mode 100644 index 00000000..2a346575 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ppp.c @@ -0,0 +1,2052 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "lwip/ip.h" /* for ip_input() */ + +#include "pppdebug.h" + +#include "randm.h" +#include "fsm.h" +#if PAP_SUPPORT +#include "pap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap.h" +#endif /* CHAP_SUPPORT */ +#include "ipcp.h" +#include "lcp.h" +#include "magic.h" +#include "auth.h" +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include "lwip/tcpip.h" +#include "lwip/api.h" +#include "lwip/snmp.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback(). + * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1. + * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded). + */ +#ifndef PPP_INPROC_MULTITHREADED +#define PPP_INPROC_MULTITHREADED (NO_SYS==0) +#endif + +/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session. + * Default is 0: call pppos_input() for received raw characters, charcater + * reception is up to the port */ +#ifndef PPP_INPROC_OWNTHREAD +#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED +#endif + +#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED + #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1" +#endif + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} PPPDevStates; + +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + +/** RX buffer size: this may be configured smaller! */ +#ifndef PPPOS_RX_BUFSIZE +#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN) +#endif + +typedef struct PPPControlRx_s { + /** unit number / ppp descriptor */ + int pd; + /** the rx file descriptor */ + sio_fd_t fd; + /** receive buffer - encoded data is stored here */ +#if PPP_INPROC_OWNTHREAD + u_char rxbuf[PPPOS_RX_BUFSIZE]; +#endif /* PPP_INPROC_OWNTHREAD */ + + /* The input packet. */ + struct pbuf *inHead, *inTail; + +#if PPPOS_SUPPORT + u16_t inProtocol; /* The input protocol code. */ + u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + PPPDevStates inState; /* The input process state. */ + char inEscaped; /* Escape next character. */ + ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ +} PPPControlRx; + +/* + * PPP interface control block. + */ +typedef struct PPPControl_s { + PPPControlRx rx; + char openFlag; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ +#endif /* PPPOS_SUPPORT */ + u16_t mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u_long lastXMit; /* Time of last transmission. */ + ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vjEnabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vjComp; /* Van Jacobson compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + struct netif netif; + + struct ppp_addrs addrs; + + void (*linkStatusCB)(void *ctx, int errCode, void *arg); + void *linkStatusCtx; + +} PPPControl; + + +/* + * Ioctl definitions. + */ + +struct npioctl { + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum NPmode mode; +}; + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +#if PPPOS_SUPPORT +#if PPP_INPROC_OWNTHREAD +static void pppInputThread(void *arg); +#endif /* PPP_INPROC_OWNTHREAD */ +static void pppDrop(PPPControlRx *pcrx); +static void pppInProc(PPPControlRx *pcrx, u_char *s, int l); +static void pppFreeCurrentInputPacket(PPPControlRx *pcrx); +#endif /* PPPOS_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +struct protent *ppp_protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL +}; + + +/* + * Buffers for outgoing packets. This must be accessed only from the appropriate + * PPP task so that it doesn't need to be protected to avoid collisions. + */ +u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ + +#if PPPOS_SUPPORT +/* + * FCS lookup table as calculated by genfcstab. + * @todo: smaller, slower implementation for lower memory footprint? + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char pppACCMMask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; + +#if PPP_INPROC_OWNTHREAD +/** Wake up the task blocked in reading from serial line (if any) */ +static void +pppRecvWakeup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd)); + if (pppControl[pd].openFlag != 0) { + sio_read_abort(pppControl[pd].fd); + } +} +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPControl* pc; +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ + pc = &pppControl[pd]; + + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } + + pc->openFlag = 0;/**/ +#endif /* PPPOS_SUPPORT */ + } + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n")); +} + +void +pppLinkDown(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/ + } +} + +/** Initiate LCP open request */ +static void +pppStart(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ + PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n")); +} + +/** LCP close request */ +static void +pppStop(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +/** Called when carrier/link is lost */ +static void +pppHup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* Initialize the PPP subsystem. */ + +struct ppp_settings ppp_settings; + +void +pppInit(void) +{ + struct protent *protp; + int i, j; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + + magicInit(); + + for (i = 0; i < NUM_PPP; i++) { + /* Initialize each protocol to the standard option set. */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); + } + } +} + +void +pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) +{ + switch(authType) { + case PPPAUTHTYPE_NONE: + default: +#ifdef LWIP_PPP_STRICT_PAP_REJECT + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; + + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if(user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if(passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } +} + +#if PPPOS_SUPPORT +/** Open a new PPP connection using the given I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. If this port + * connects to a modem, the modem connection must be + * established before calling this. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + * + * pppOpen() is directly defined to this function. + */ +int +pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + /* input pbuf left over from last session? */ + pppFreeCurrentInputPacket(&pc->rx); + /* @todo: is this correct or do I overwrite something? */ + memset(pc, 0, sizeof(PPPControl)); + pc->rx.pd = pd; + pc->rx.fd = fd; + + pc->openFlag = 1; + pc->fd = fd; + +#if VJ_SUPPORT + vj_compress_init(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */ + pc->outACCM[15] = 0x60; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd)); + pppStart(pd); +#if PPP_INPROC_OWNTHREAD + sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); +#endif /* PPP_INPROC_OWNTHREAD */ + } + + return pd; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void +pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + memset(pc, 0, sizeof(PPPControl)); + pc->openFlag = 1; + pc->ethif = ethif; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +/* Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. */ +int +pppClose(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + PPPDEBUG(LOG_DEBUG, ("pppClose() called\n")); + + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +pppSigHUP(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + pppHup(pd); +} + +#if PPPOS_SUPPORT +static void +nPut(PPPControl *pc, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG(LOG_WARNING, + ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c)); + LINK_STATS_INC(link.err); + pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ + snmp_inc_ifoutdiscards(&pc->netif); + pbuf_free(nb); + return; + } + } + + snmp_add_ifoutoctets(&pc->netif, nb->tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * pppAppend - append given character to end of given pbuf. If outACCM + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (outACCM && ESCAPE_P(*outACCM, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + u16_t tot_len; + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + tot_len = pb->tot_len; + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + +/* Send a packet on the given connection. */ +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr) +{ + int pd = (int)(size_t)netif->state; + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_short protocol = PPP_IP; + u_int fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u_char c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, PPP_IP, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vjEnabled) { + switch (vj_compress_tcp(&pc->vjComp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + pbuf_free(headMB); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->lastXMit = sys_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol)); + + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return ERR_OK; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +pppIOCtl(int pd, int cmd, void *arg) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->errCode = *(int *)arg; + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->errCode); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: /* Get the fd associated with the ppp */ + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; + } + } + + return st; +} + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short +pppMTU(int pd) +{ + PPPControl *pc = &pppControl[pd]; + u_short st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + MEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, (u16_t)n); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int +pppWrite(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_char c; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->lastXMit = sys_jiffies(); + + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len)); + /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n", + unit, + pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(int unit, ext_accm *accm) +{ + SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm)); + PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", + unit, + pppControl[unit].outACCM[0], + pppControl[unit].outACCM[1], + pppControl[unit].outACCM[2], + pppControl[unit].outACCM[3])); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ + SYS_ARCH_PROTECT(lev); + for (i = 0; i < 32 / 8; i++) { + /* @todo: does this work? ext_accm has been modified from pppd! */ + pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8)); + } + SYS_ARCH_UNPROTECT(lev); + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n", + unit, + pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3])); +} + +#if 0 +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr) +{ + return 0; /* XXX Currently no compression. */ +} + +/* + * ccp_flags_set - inform kernel about the current state of CCP. + */ +void +ccp_flags_set(int unit, int isopen, int isup) +{ + /* XXX */ +} + +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(int unit) +{ + /* XXX */ + return 0; +} +#endif + +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); + + return 0; +} + + +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t +GetMask(u32_t addr) +{ + u32_t mask, nmask; + + addr = htonl(addr); + if (IP_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IP_CLASSA_NET; + } else if (IP_CLASSB(addr)) { + nmask = IP_CLASSB_NET; + } else { + nmask = IP_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = PP_HTONL(0xffffff00UL) | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid) +{ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPControl *pc = &pppControl[pd]; + + pc->vjEnabled = vjcomp; + pc->vjComp.compressSlot = cidcomp; + pc->vjComp.maxSlotIndex = maxcid; + PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + LWIP_UNUSED_ARG(pd); + LWIP_UNUSED_ARG(vjcomp); + LWIP_UNUSED_ARG(cidcomp); + LWIP_UNUSED_ARG(maxcid); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +/* + * pppifNetifInit - netif init callback + */ +static err_t +pppifNetifInit(struct netif *netif) +{ + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = pppMTU((int)(size_t)netif->state); + netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP; +#if LWIP_NETIF_HOSTNAME + /* @todo: Initialize interface hostname */ + /* netif_set_hostname(netif, "lwip"); */ +#endif /* LWIP_NETIF_HOSTNAME */ + return ERR_OK; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, + &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); + pc->if_up = 1; + pc->errCode = PPPERR_NONE; + + PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); + } + } else { + st = 0; + PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd)); + } + } + + return st; +} + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int +sifnpmode(int u, int proto, enum NPmode mode) +{ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + /* make sure the netif status callback is called */ + netif_set_down(&pc->netif); + netif_remove(&pc->netif); + PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); + } + } + return st; +} + +/** + * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS + */ +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; +} + +/** + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? + */ +int +cifaddr( int pd, u32_t o, u32_t h) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } + + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } + + return st; +} + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD +/* The main PPP process function. This implements the state machine according + * to section 4 of RFC 1661: The Point-To-Point Protocol. */ +static void +pppInputThread(void *arg) +{ + int count; + PPPControlRx *pcrx = arg; + + while (lcp_phase[pcrx->pd] != PHASE_DEAD) { + count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE); + if(count > 0) { + pppInProc(pcrx, pcrx->rxbuf, count); + } else { + /* nothing received, give other tasks a chance to run */ + sys_msleep(1); + } + } +} +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */ + +#if PPPOE_SUPPORT + +void +pppOverEthernetInitFailed(int pd) +{ + PPPControl* pc; + + pppHup(pd); + pppStop(pd); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd)); + pppStart(pd); + } else { + pppOverEthernetInitFailed(pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf * +pppSingleBuf(struct pbuf *p) +{ + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG(LOG_ERR, + ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +/** Input helper struct, must be packed since it is stored to pbuf->payload, + * which might be unaligned. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppInputHeader { + PACK_STRUCT_FIELD(int unit); + PACK_STRUCT_FIELD(u16_t proto); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +static void +pppInput(void *arg) +{ + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; + + pd = ((struct pppInputHeader *)nb->payload)->unit; + protocol = ((struct pppInputHeader *)nb->payload)->proto; + + if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + snmp_inc_ifinucastpkts(&pppControl[pd].netif); + snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd])); + goto drop; + } + } + + switch(protocol) { + case PPP_VJC_COMP: /* VJ compressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, + ("pppInput[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = pppSingleBuf(nb); + (*protp->input)(pd, nb->payload, nb->len); + PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd)); + goto out; + } + } + + /* No handler for this protocol so reject the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } +#if BYTE_ORDER == LITTLE_ENDIAN + protocol = htons(protocol); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); + lcp_sprotrej(pd, nb->payload, nb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + +out: + pbuf_free(nb); + return; +} + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +pppFreeCurrentInputPacket(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { + if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) { + pbuf_free(pcrx->inTail); + } + pbuf_free(pcrx->inHead); + pcrx->inHead = NULL; + } + pcrx->inTail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +pppDrop(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload)); +#endif + PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead)); + } + pppFreeCurrentInputPacket(pcrx); +#if VJ_SUPPORT + vj_uncompress_err(&pppControl[pcrx->pd].vjComp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); +} + +#if !PPP_INPROC_OWNTHREAD +/** Pass received raw characters to PPPoS to be decoded. This function is + * thread-safe and can be called from a dedicated RX-thread or from a main-loop. + * + * @param pd PPP descriptor index, returned by pppOpen() + * @param data received data + * @param len length of received data + */ +void +pppos_input(int pd, u_char* data, int len) +{ + pppInProc(&pppControl[pd].rx, data, len); +} +#endif + +/** + * Process a received octet string. + */ +static void +pppInProc(PPPControlRx *pcrx, u_char *s, int l) +{ + struct pbuf *nextNBuf; + u_char curChar; + u_char escaped; + SYS_ARCH_DECL_PROTECT(lev); + + PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l)); + while (l-- > 0) { + curChar = *s++; + + SYS_ARCH_PROTECT(lev); + escaped = ESCAPE_P(pcrx->inACCM, curChar); + SYS_ARCH_UNPROTECT(lev); + /* Handle special characters. */ + if (escaped) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pcrx->inEscaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pcrx->inState <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pcrx->inState < PDDATA) { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping incomplete packet %d\n", + pcrx->pd, pcrx->inState)); + LINK_STATS_INC(link.lenerr); + pppDrop(pcrx); + /* If the fcs is invalid, drop the packet. */ + } else if (pcrx->inFCS != PPP_GOODFCS) { + PPPDEBUG(LOG_INFO, + ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n", + pcrx->pd, pcrx->inFCS, pcrx->inProtocol)); + /* Note: If you get lots of these, check for UART frame errors or try different baud rate */ + LINK_STATS_INC(link.chkerr); + pppDrop(pcrx); + /* Otherwise it's a good packet so pass it on. */ + } else { + struct pbuf *inp; + /* Trim off the checksum. */ + if(pcrx->inTail->len > 2) { + pcrx->inTail->len -= 2; + + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + } else { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + + pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + inp = pcrx->inHead; + /* Packet consumed, release our references. */ + pcrx->inHead = NULL; + pcrx->inTail = NULL; +#if PPP_INPROC_MULTITHREADED + if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) { + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd)); + pbuf_free(inp); + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); + } +#else /* PPP_INPROC_MULTITHREADED */ + pppInput(inp); +#endif /* PPP_INPROC_MULTITHREADED */ + } + + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + pcrx->inState = PDADDRESS; + pcrx->inEscaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pcrx->inEscaped) { + pcrx->inEscaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pcrx->inState) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pcrx->inState = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pcrx->inState = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar)); + pcrx->inState = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pcrx->inProtocol = curChar; + pcrx->inState = PDDATA; + } else { + pcrx->inProtocol = (u_int)curChar << 8; + pcrx->inState = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pcrx->inProtocol |= curChar; + pcrx->inState = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) { + if (pcrx->inTail != NULL) { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + /* give up the inTail reference now */ + pcrx->inTail = NULL; + } + } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd)); + LINK_STATS_INC(link.memerr); + pppDrop(pcrx); + pcrx->inState = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pcrx->inHead == NULL) { + struct pppInputHeader *pih = nextNBuf->payload; + + pih->unit = pcrx->pd; + pih->proto = pcrx->inProtocol; + + nextNBuf->len += sizeof(*pih); + + pcrx->inHead = nextNBuf; + } + pcrx->inTail = nextNBuf; + } + /* Load character into buffer. */ + ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar); + } + } /* while (l-- > 0), all bytes processed */ + + avRandomize(); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void +pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + pppInput(pb); + return; + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ + +#if LWIP_NETIF_STATUS_CALLBACK +/** Set the status callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param status_callback pointer to the status callback function + * + * @see netif_set_status_callback + */ +void +ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback) +{ + netif_set_status_callback(&pppControl[pd].netif, status_callback); +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** Set the link callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param link_callback pointer to the link callback function + * + * @see netif_set_link_callback + */ +void +ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback) +{ + netif_set_link_callback(&pppControl[pd].netif, link_callback); +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ppp.h b/external/badvpn_dns/lwip/src/netif/ppp/ppp.h new file mode 100644 index 00000000..08d6e62d --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ppp.h @@ -0,0 +1,201 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + + +#ifndef __u_char_defined + +/* Type definitions for BSD code. */ +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; + +#endif + + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +struct ppp_addrs { + ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* Initialize the PPP subsystem. */ +void pppInit(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum pppAuthType { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); + +/* Link status callback function prototype */ +typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg); + +#if PPPOS_SUPPORT +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOE_SUPPORT */ + +/* for source code compatibility */ +#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int pppClose(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void pppSigHUP(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int pppIOCtl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short pppMTU(int pd); + +#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD +/* + * PPP over Serial: this is the input function to be called for received data. + * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking + * sio_read() is used, so this is deactivated. + */ +void pppos_input(int pd, u_char* data, int len); +#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */ + + +#if LWIP_NETIF_STATUS_CALLBACK +/* Set an lwIP-style status-callback for the selected PPP device */ +void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK +/* Set an lwIP-style link-callback for the selected PPP device */ +void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ppp_impl.h b/external/badvpn_dns/lwip/src/netif/ppp/ppp_impl.h new file mode 100644 index 00000000..89aea60b --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ppp_impl.h @@ -0,0 +1,363 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_IMPL_H +#define PPP_IMPL_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + +/** Some defines for code we skip compared to the original pppd. + * These are just here to minimise the use of the ugly "#if 0". */ +#define PPP_ADDITIONAL_CALLBACKS 0 + +/** Some error checks to test for unsupported code */ +#if CBCP_SUPPORT +#error "CBCP is not supported in lwIP PPP" +#endif +#if CCP_SUPPORT +#error "CCP is not supported in lwIP PPP" +#endif + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if PPP_ADDITIONAL_CALLBACKS + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + int enabled_flag; /* 0 if protocol is disabled */ + char *name; /* Text name of protocol */ +#if PPP_ADDITIONAL_CALLBACKS + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, u16_t, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, u8_t, u8_t); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_IMPL_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/ppp_oe.c b/external/badvpn_dns/lwip/src/netif/ppp/ppp_oe.c new file mode 100644 index 00000000..fdf52ae2 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1132 @@ +/***************************************************************************** +* ppp_oe.c - PPP Over Ethernet implementation for lwIP. +* +* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 06-01-01 Marc Boucher +* Ported to lwIP. +*****************************************************************************/ + + + +/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Martin Husemann . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp_oe.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "lwip/timers.h" +#include "lwip/memp.h" + +#include +#include + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (u8_t)((VAL) / 256); \ + *(PTR)++ = (u8_t)((VAL) % 256) + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#error "PPPOE_SERVER is not yet supported under lwIP!" +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +#ifndef PPPOE_ERRORSTRING_LEN +#define PPPOE_ERRORSTRING_LEN 64 +#endif +static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN]; + + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +/** linked list of created pppoe interfaces */ +static struct pppoe_softc *pppoe_softc_list; + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF); + if (sc == NULL) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + /* put the new interface at the head of the list */ + sc->next = pppoe_softc_list; + pppoe_softc_list = sc; + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc *sc, *prev = NULL; + + for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + sys_untimeout(pppoe_timeout, sc); + if (prev == NULL) { + /* remove sc from the head of the list */ + pppoe_softc_list = sc->next; + } else { + /* remove sc from the list */ + prev->next = sc->next; + } + +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } +#endif /* PPPOE_TODO */ + memp_free(MEMP_PPPOE_IF, sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (pppoe_softc_list == NULL) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(struct pppoe_softc *sc) +{ + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + u8_t *ac_cookie; + u16_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + if (errortag && len) { + u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1); + strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len); + pppoe_error_tmp[error_len-1] = '\0'; + printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + sys_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + (u16_t)ph->code, session); + } else { + printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len; +#ifdef PPPOE_TODO + int l1 = 0, l2 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + l1 = (int)strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = (int)strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } +#endif /* PPPOE_TODO */ + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } +#endif /* PPPOE_TODO */ + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + sys_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; +#ifdef PPPOE_TODO + size_t l1 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } +#endif /* PPPOE_TODO */ + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/external/badvpn_dns/lwip/src/netif/ppp/pppdebug.h b/external/badvpn_dns/lwip/src/netif/ppp/pppdebug.h new file mode 100644 index 00000000..81349971 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/pppdebug.h @@ -0,0 +1,73 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/* Trace levels. */ +#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_INFO (PPP_DEBUG) +#define LOG_DETAIL (PPP_DEBUG) +#define LOG_DEBUG (PPP_DEBUG) + + +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG + +#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b) +#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b) +#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b) + +#else /* PPP_DEBUG */ + +#define AUTHDEBUG(a, b) +#define IPCPDEBUG(a, b) +#define UPAPDEBUG(a, b) +#define LCPDEBUG(a, b) +#define FSMDEBUG(a, b) +#define CHAPDEBUG(a, b) +#define PPPDEBUG(a, b) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/randm.c b/external/badvpn_dns/lwip/src/netif/ppp/randm.c new file mode 100644 index 00000000..b736091f --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/randm.c @@ -0,0 +1,249 @@ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "md5.h" +#include "randm.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include + +#if MD5_SUPPORT /* this module depends on MD5 */ +#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ +static long randCount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Since this is to be called on power up, we don't have much + * system randomess to work with. Here all we use is the + * real-time clock. We'll accumulate more randomness as soon + * as things start happening. + */ +void +avRandomInit() +{ + avChurnRand(NULL, 0); +} + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +void +avChurnRand(char *randData, u32_t randLen) +{ + MD5_CTX md5; + + /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */ + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + if (randData) { + MD5Update(&md5, (u_char *)randData, randLen); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sysData; + + /* Load sysData fields here. */ + MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); + } + MD5Final((u_char *)randPool, &md5); +/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */ +} + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Note: It's important that there be sufficient randomness in randPool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call churnRand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * randCount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void +avGenRand(char *buf, u32_t bufLen) +{ + MD5_CTX md5; + u_char tmp[16]; + u32_t n; + + while (bufLen > 0) { + n = LWIP_MIN(bufLen, RANDPOOLSZ); + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); + MD5Final(tmp, &md5); + randCount++; + MEMCPY(buf, tmp, n); + buf += n; + bufLen -= n; + } +} + +/* + * Return a new random number. + */ +u32_t +avRandom() +{ + u32_t newRand; + + avGenRand((char *)&newRand, sizeof(newRand)); + + return newRand; +} + +#else /* MD5_SUPPORT */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int avRandomized = 0; /* Set when truely randomized. */ +static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void +avRandomInit() +{ +#if 0 + /* Get a pointer into the last 4 bytes of clockBuf. */ + u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + + /* + * Initialize our seed using the real-time clock, the idle + * counter, the millisecond timer, and the hardware timer + * tick counter. The real-time clock and the hardware + * tick counter are the best sources of randomness but + * since the tick counter is only 16 bit (and truncated + * at that), the idle counter and millisecond timer + * (which may be small values) are added to help + * randomize the lower 16 bits of the seed. + */ + readClk(); + avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr + + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; +#else + avRandomSeed += sys_jiffies(); /* XXX */ +#endif + + /* Initialize the Borland random number generator. */ + srand((unsigned)avRandomSeed); +} + +/* + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void +avRandomize(void) +{ + static u32_t last_jiffies; + + if (!avRandomized) { + avRandomized = !0; + avRandomInit(); + /* The initialization function also updates the seed. */ + } else { + /* avRandomSeed += (avRandomSeed << 16) + TM1; */ + avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t +avRandom() +{ + return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); +} + +#endif /* MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/randm.h b/external/badvpn_dns/lwip/src/netif/ppp/randm.h new file mode 100644 index 00000000..a0984b02 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/randm.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems Inc. +* +* The authors hereby grant permission to use, copy, modify, distribute, +* and license this software and its documentation for any purpose, provided +* that existing copyright notices are retained in all copies and that this +* notice and the following disclaimer are included verbatim in any +* distributions. No written agreement, license, or royalty fee is required +* for any of the authorized uses. +* +* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +****************************************************************************** +* REVISION HISTORY +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#ifndef RANDM_H +#define RANDM_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * Initialize the random number generator. + */ +void avRandomInit(void); + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + */ +void avChurnRand(char *randData, u32_t randLen); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +#if MD5_SUPPORT +#define avRandomize() avChurnRand(NULL, 0) +#else /* MD5_SUPPORT */ +void avRandomize(void); +#endif /* MD5_SUPPORT */ + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void avGenRand(char *buf, u32_t bufLen); + +/* + * Return a new random number. + */ +u32_t avRandom(void); + + +#endif /* RANDM_H */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/readme.txt b/external/badvpn_dns/lwip/src/netif/ppp/readme.txt new file mode 100644 index 00000000..5be41b90 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/readme.txt @@ -0,0 +1,21 @@ +About the PPP code: + +The PPP code is not our "own" code - we just copied it from pppd (http://ppp.samba.org/) and adapted it to lwIP. +Unfortunately, not many here know their way around it too well. Back in 2009, we took the effort to see which +version of pppd our code relates to and we're pretty much on 2.3.11 with some bugs from 2.4.x backported. + +Aside from simple code adaptions, there are some files that are different, however: +- chpms.c/.h are named chap_ms.c/.h in the original pppd 2.3.11 sources +- pap.c/.h are named upap.c/.h in the original pppd 2.3.11 sources +- randm.c is a random generator not included in the original pppd +- magic.c does not use the C library's random functions, but uses randm.c instead +- vj.c/.h is an implementation of the Van Jacobson header compression algorithm adapted to lwIP pbufs, + probably copied from one of the vjcompress.c files from pppd. +- ppp.c, ppp.h and ppp_impl.h contain the adaption from pppd to lwIP. This is the "OS"-dependent part like there + is an implementation for linux, xBSD etc. in the pppd sources. +- ppp_oe.c is Marc Boucher's implementation based on NetBSD's if_pppoe.c + +There is of course potential for bugs in it, but when analyzing of reporting bugs, it is strongly encouraged to +compare the code in question to pppd 2.3.11 (our basis) and newer versions (perhaps it's already fixed?) and to +share this knowledge with us when reporting a bug. + diff --git a/external/badvpn_dns/lwip/src/netif/ppp/vj.c b/external/badvpn_dns/lwip/src/netif/ppp/vj.c new file mode 100644 index 00000000..40fdad13 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/vj.c @@ -0,0 +1,652 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "vj.h" + +#include + +#if VJ_SUPPORT + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_char i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u_short tmp = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip_hdr *ip = (struct ip_hdr *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = IPH_HL(ip); + register struct tcp_hdr *oth; + register struct tcp_hdr *th; + register u_short deltaS, deltaA; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (IPH_PROTO(ip) != IP_PROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcp_hdr *)&((long *)ip)[hlen]; + if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src) + || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip_addr_cmp(&ip->src, &cs->cs_ip.src) + && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (TCPH_FLAGS(th) & TCP_URG) { + deltaS = ntohs(th->urgp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->urgp != oth->urgp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && + ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip))); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (TCPH_FLAGS(th) & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->chksum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)(changes | NEW_C); + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)changes; + } + *cp++ = (u_char)(deltaA >> 8); + *cp++ = (u_char)deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + IPH_PROTO_SET(ip, cs->cs_id); + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip_hdr *ip; + + ip = (struct ip_hdr *)nb->payload; + hlen = IPH_HL(ip) << 2; + if (IPH_PROTO(ip) >= MAX_SLOTS + || hlen + sizeof(struct tcp_hdr) > nb->len + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + IPH_PROTO(ip), hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; + comp->flags &=~ VJF_TOSS; + IPH_PROTO_SET(ip, IP_PROTO_TCP); + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = (u_short)hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcp_hdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = IPH_HL(&cs->cs_ip) << 2; + th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->chksum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + TCPH_SET_FLAG(th, TCP_PSH); + } else { + TCPH_UNSET_FLAG(th, TCP_PSH); + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->ackno) + i; + th->ackno = htonl(tmp); + tmp = ntohl(th->seqno) + i; + th->seqno = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + th->seqno = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + TCPH_SET_FLAG(th, TCP_URG); + DECODEU(th->urgp); + } else { + TCPH_UNSET_FLAG(th, TCP_URG); + } + if (changes & NEW_W) { + DECODES(th->wnd); + } + if (changes & NEW_A) { + DECODEL(th->ackno); + } + if (changes & NEW_S) { + DECODEL(th->seqno); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip._id); + } else { + IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1); + IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip))); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp)); +#else + IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen)); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + IPH_CHKSUM_SET(&cs->cs_ip, 0); + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp)); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* VJ_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/external/badvpn_dns/lwip/src/netif/ppp/vj.h b/external/badvpn_dns/lwip/src/netif/ppp/vj.h new file mode 100644 index 00000000..fad12136 --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/ppp/vj.h @@ -0,0 +1,156 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef VJ_H +#define VJ_H + +#include "lwip/ip.h" +#include "lwip/tcp_impl.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ diff --git a/external/badvpn_dns/lwip/src/netif/slipif.c b/external/badvpn_dns/lwip/src/netif/slipif.c new file mode 100644 index 00000000..137ba89d --- /dev/null +++ b/external/badvpn_dns/lwip/src/netif/slipif.c @@ -0,0 +1,546 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + * Simon Goldschmidt + * + * Usage: This netif can be used in three ways: + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received. + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input(). + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" +#include "lwip/sys.h" + +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ + +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE +#define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u8_t state; + u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output(struct netif *netif, struct pbuf *p) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); + priv = netif->state; + + /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + /* normal byte - no need for escaping */ + sio_send(c, priv->sd); + break; + } + } + } + /* End with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} + +#if LWIP_IPV6 +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV6 */ + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) +{ + struct slipif_priv *priv; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + priv->state = SLIP_RECV_NORMAL; + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + return NULL; +} + +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + u8_t c; + struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + + while (1) { + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } + } +} +#endif /* SLIP_USE_RX_THREAD */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + u8_t sio_num; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output_v4; +#if LWIP_IPV6 + netif->output_ip6 = slipif_output_v6; +#endif /* LWIP_IPV6 */ + netif->mtu = SLIP_MAX_SIZE; + netif->flags |= NETIF_FLAG_POINTTOPOINT; + + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); + +#if SLIP_USE_RX_THREAD + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + u8_t c; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while(q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); + } +} +#endif /* SLIP_RX_FROM_ISR */ + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/external/badvpn_dns/lwip/test/unit/core/test_mem.c b/external/badvpn_dns/lwip/test/unit/core/test_mem.c new file mode 100644 index 00000000..d3a5d540 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/core/test_mem.c @@ -0,0 +1,73 @@ +#include "test_mem.h" + +#include "lwip/mem.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !MEM_STATS +#error "This tests needs MEM-statistics enabled" +#endif +#if LWIP_DNS +#error "This test needs DNS turned off (as it mallocs on init)" +#endif + +/* Setups/teardown functions */ + +static void +mem_setup(void) +{ +} + +static void +mem_teardown(void) +{ +} + + +/* Test functions */ + +/** Call mem_malloc, mem_free and mem_trim and check stats */ +START_TEST(test_mem_one) +{ +#define SIZE1 16 +#define SIZE1_2 12 +#define SIZE2 16 + void *p1, *p2; + mem_size_t s1, s2; + LWIP_UNUSED_ARG(_i); + +#if LWIP_DNS + fail("This test needs DNS turned off (as it mallocs on init)"); +#endif + + fail_unless(lwip_stats.mem.used == 0); + + p1 = mem_malloc(SIZE1); + fail_unless(p1 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE1); + s1 = lwip_stats.mem.used; + + p2 = mem_malloc(SIZE2); + fail_unless(p2 != NULL); + fail_unless(lwip_stats.mem.used >= SIZE2 + s1); + s2 = lwip_stats.mem.used; + + mem_trim(p1, SIZE1_2); + + mem_free(p2); + fail_unless(lwip_stats.mem.used <= s2 - SIZE2); + + mem_free(p1); + fail_unless(lwip_stats.mem.used == 0); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +mem_suite(void) +{ + TFun tests[] = { + test_mem_one + }; + return create_suite("MEM", tests, sizeof(tests)/sizeof(TFun), mem_setup, mem_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/core/test_mem.h b/external/badvpn_dns/lwip/test/unit/core/test_mem.h new file mode 100644 index 00000000..13803edc --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/core/test_mem.h @@ -0,0 +1,8 @@ +#ifndef __TEST_MEM_H__ +#define __TEST_MEM_H__ + +#include "../lwip_check.h" + +Suite *mem_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/core/test_pbuf.c b/external/badvpn_dns/lwip/test/unit/core/test_pbuf.c new file mode 100644 index 00000000..29110784 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/core/test_pbuf.c @@ -0,0 +1,73 @@ +#include "test_pbuf.h" + +#include "lwip/pbuf.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS +#error "This tests needs MEM- and MEMP-statistics enabled" +#endif +#if LWIP_DNS +#error "This test needs DNS turned off (as it mallocs on init)" +#endif + +/* Setups/teardown functions */ + +static void +pbuf_setup(void) +{ +} + +static void +pbuf_teardown(void) +{ +} + + +/* Test functions */ + +/** Call pbuf_copy on a pbuf with zero length */ +START_TEST(test_pbuf_copy_zero_pbuf) +{ + struct pbuf *p1, *p2, *p3; + err_t err; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.mem.used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); + + p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM); + fail_unless(p1 != NULL); + fail_unless(p1->ref == 1); + + p2 = pbuf_alloc(PBUF_RAW, 2, PBUF_POOL); + fail_unless(p2 != NULL); + fail_unless(p2->ref == 1); + p2->len = p2->tot_len = 0; + + pbuf_cat(p1, p2); + fail_unless(p1->ref == 1); + fail_unless(p2->ref == 1); + + p3 = pbuf_alloc(PBUF_RAW, p1->tot_len, PBUF_POOL); + err = pbuf_copy(p3, p1); + fail_unless(err == ERR_VAL); + + pbuf_free(p1); + pbuf_free(p3); + fail_unless(lwip_stats.mem.used == 0); + + fail_unless(lwip_stats.mem.used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +pbuf_suite(void) +{ + TFun tests[] = { + test_pbuf_copy_zero_pbuf + }; + return create_suite("PBUF", tests, sizeof(tests)/sizeof(TFun), pbuf_setup, pbuf_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/core/test_pbuf.h b/external/badvpn_dns/lwip/test/unit/core/test_pbuf.h new file mode 100644 index 00000000..b2715ade --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/core/test_pbuf.h @@ -0,0 +1,8 @@ +#ifndef __TEST_PBUF_H__ +#define __TEST_PBUF_H__ + +#include "../lwip_check.h" + +Suite *pbuf_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.c b/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.c new file mode 100644 index 00000000..4b40de89 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.c @@ -0,0 +1,916 @@ +#include "test_dhcp.h" + +#include "lwip/netif.h" +#include "lwip/dhcp.h" +#include "netif/etharp.h" + +struct netif net_test; + +static const u8_t broadcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static const u8_t magic_cookie[] = { 0x63, 0x82, 0x53, 0x63 }; + +static u8_t dhcp_offer[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */ + 0x00, 0x0F, 0xEE, 0x30, 0xAB, 0x22, /* From Remote host */ + 0x08, 0x00, /* Protocol: IP */ + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */ + 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */ + + 0x02, /* Type == Boot reply */ + 0x01, 0x06, /* Hw Ethernet, 6 bytes addrlen */ + 0x00, /* 0 hops */ + 0xAA, 0xAA, 0xAA, 0xAA, /* Transaction id, will be overwritten */ + 0x00, 0x00, /* 0 seconds elapsed */ + 0x00, 0x00, /* Flags (unicast) */ + 0x00, 0x00, 0x00, 0x00, /* Client ip */ + 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */ + 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server ip */ + 0x00, 0x00, 0x00, 0x00, /* relay agent */ + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MAC addr + padding */ + + /* Empty server name and boot file name */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x63, 0x82, 0x53, 0x63, /* Magic cookie */ + 0x35, 0x01, 0x02, /* Message type: Offer */ + 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Server identifier (IP) */ + 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */ + 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */ + 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Subnet mask */ + 0xff, /* End option */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */ +}; + +static u8_t dhcp_ack[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */ + 0x00, 0x0f, 0xEE, 0x30, 0xAB, 0x22, /* From remote host */ + 0x08, 0x00, /* Proto IP */ + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */ + 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */ + 0x02, /* Bootp reply */ + 0x01, 0x06, /* Hw type Eth, len 6 */ + 0x00, /* 0 hops */ + 0xAA, 0xAA, 0xAA, 0xAA, + 0x00, 0x00, /* 0 seconds elapsed */ + 0x00, 0x00, /* Flags (unicast) */ + 0x00, 0x00, 0x00, 0x00, /* Client IP */ + 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */ + 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server IP */ + 0x00, 0x00, 0x00, 0x00, /* Relay agent */ + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Macaddr + padding */ + + /* Empty server name and boot file name */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x63, 0x82, 0x53, 0x63, /* Magic cookie */ + 0x35, 0x01, 0x05, /* Dhcp message type ack */ + 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server identifier */ + 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */ + 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */ + 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Netmask */ + 0xff, /* End marker */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Padding */ +}; + +static const u8_t arpreply[] = { + 0x00, 0x23, 0xC1, 0xDE, 0xD0, 0x0D, /* dst mac */ + 0x00, 0x32, 0x44, 0x20, 0x01, 0x02, /* src mac */ + 0x08, 0x06, /* proto arp */ + 0x00, 0x01, /* hw eth */ + 0x08, 0x00, /* proto ip */ + 0x06, /* hw addr len 6 */ + 0x04, /* proto addr len 4 */ + 0x00, 0x02, /* arp reply */ + 0x00, 0x32, 0x44, 0x20, 0x01, 0x02, /* sender mac */ + 0xc3, 0xaa, 0xbd, 0xc8, /* sender ip */ + 0x00, 0x23, 0xC1, 0xDE, 0xD0, 0x0D, /* target mac */ + 0x00, 0x00, 0x00, 0x00, /* target ip */ +}; + +static int txpacket; +static enum tcase { + TEST_LWIP_DHCP, + TEST_LWIP_DHCP_NAK, + TEST_LWIP_DHCP_RELAY, + TEST_LWIP_DHCP_NAK_NO_ENDMARKER, +} tcase; + +static int debug = 0; +static void setdebug(int a) {debug = a;} + +static int tick = 0; +static void tick_lwip(void) +{ + tick++; + if (tick % 5 == 0) { + dhcp_fine_tmr(); + } + if (tick % 600 == 0) { + dhcp_coarse_tmr(); + } +} + +static void send_pkt(struct netif *netif, const u8_t *data, u32_t len) +{ + struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + struct pbuf *q; + + if (debug) { + /* Dump data */ + u32_t i; + printf("RX data (len %d)", p->tot_len); + for (i = 0; i < len; i++) { + printf(" %02X", data[i]); + } + printf("\n"); + } + + fail_unless(p != NULL); + for(q = p; q != NULL; q = q->next) { + memcpy(q->payload, data, q->len); + data += q->len; + } + netif->input(p, netif); +} + +static err_t lwip_tx_func(struct netif *netif, struct pbuf *p); + +static err_t testif_init(struct netif *netif) +{ + netif->name[0] = 'c'; + netif->name[1] = 'h'; + netif->output = etharp_output; + netif->linkoutput = lwip_tx_func; + netif->mtu = 1500; + netif->hwaddr_len = 6; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + netif->hwaddr[0] = 0x00; + netif->hwaddr[1] = 0x23; + netif->hwaddr[2] = 0xC1; + netif->hwaddr[3] = 0xDE; + netif->hwaddr[4] = 0xD0; + netif->hwaddr[5] = 0x0D; + + return ERR_OK; +} + +static void dhcp_setup(void) +{ + txpacket = 0; +} + +static void dhcp_teardown(void) +{ +} + +static void check_pkt(struct pbuf *p, u32_t pos, const u8_t *mem, u32_t len) +{ + u8_t *data; + + fail_if((pos + len) > p->tot_len); + while (pos > p->len && p->next) { + pos -= p->len; + p = p->next; + } + fail_if(p == NULL); + fail_unless(pos + len <= p->len); /* All data we seek within same pbuf */ + + data = p->payload; + fail_if(memcmp(&data[pos], mem, len), "data at pos %d, len %d in packet %d did not match", pos, len, txpacket); +} + +static void check_pkt_fuzzy(struct pbuf *p, u32_t startpos, const u8_t *mem, u32_t len) +{ + int found; + u32_t i; + u8_t *data; + + fail_if((startpos + len) > p->tot_len); + while (startpos > p->len && p->next) { + startpos -= p->len; + p = p->next; + } + fail_if(p == NULL); + fail_unless(startpos + len <= p->len); /* All data we seek within same pbuf */ + + found = 0; + data = p->payload; + for (i = startpos; i <= (p->len - len); i++) { + if (memcmp(&data[i], mem, len) == 0) { + found = 1; + break; + } + } + fail_unless(found); +} + +static err_t lwip_tx_func(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &net_test); + txpacket++; + + if (debug) { + struct pbuf *pp = p; + /* Dump data */ + printf("TX data (pkt %d, len %d, tick %d)", txpacket, p->tot_len, tick); + do { + int i; + for (i = 0; i < pp->len; i++) { + printf(" %02X", ((u8_t *) pp->payload)[i]); + } + if (pp->next) { + pp = pp->next; + } + } while (pp->next); + printf("\n"); + } + + switch (tcase) { + case TEST_LWIP_DHCP: + switch (txpacket) { + case 1: + case 2: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + if (txpacket == 1) { + u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; + check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); + } else if (txpacket == 2) { + u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + u8_t requested_ipaddr[] = { 0x32, 0x04, 0xc3, 0xaa, 0xbd, 0xc8 }; /* Ask for offered IP */ + + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + } + break; + } + case 3: + case 4: + case 5: + { + const u8_t arpproto[] = { 0x08, 0x06 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ + break; + } + } + break; + + case TEST_LWIP_DHCP_NAK: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8_t dhcp_nak_opt[] = { 0x35, 0x01, 0x04 }; + const u8_t requested_ipaddr[] = { 0x32, 0x04, 0xc3, 0xaa, 0xbd, 0xc8 }; /* offered IP */ + + fail_unless(txpacket == 4); + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + check_pkt_fuzzy(p, 282, dhcp_nak_opt, sizeof(dhcp_nak_opt)); /* NAK the ack */ + + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + break; + } + + case TEST_LWIP_DHCP_RELAY: + switch (txpacket) { + case 1: + case 2: + { + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + if (txpacket == 1) { + u8_t dhcp_discover_opt[] = { 0x35, 0x01, 0x01 }; + check_pkt_fuzzy(p, 282, dhcp_discover_opt, sizeof(dhcp_discover_opt)); + } else if (txpacket == 2) { + u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + u8_t requested_ipaddr[] = { 0x32, 0x04, 0x4f, 0x8a, 0x33, 0x05 }; /* Ask for offered IP */ + + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + check_pkt_fuzzy(p, 282, requested_ipaddr, sizeof(requested_ipaddr)); + } + break; + } + case 3: + case 4: + case 5: + case 6: + { + const u8_t arpproto[] = { 0x08, 0x06 }; + + check_pkt(p, 0, broadcast, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */ + break; + } + case 7: + { + const u8_t fake_arp[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab }; + const u8_t ipproto[] = { 0x08, 0x00 }; + const u8_t bootp_start[] = { 0x01, 0x01, 0x06, 0x00}; /* bootp request, eth, hwaddr len 6, 0 hops */ + const u8_t ipaddrs[] = { 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const u8_t dhcp_request_opt[] = { 0x35, 0x01, 0x03 }; + + check_pkt(p, 0, fake_arp, 6); /* eth level dest: broadcast */ + check_pkt(p, 6, netif->hwaddr, 6); /* eth level src: unit mac */ + + check_pkt(p, 12, ipproto, sizeof(ipproto)); /* eth level proto: ip */ + + check_pkt(p, 42, bootp_start, sizeof(bootp_start)); + + check_pkt(p, 53, ipaddrs, sizeof(ipaddrs)); + + check_pkt(p, 70, netif->hwaddr, 6); /* mac addr inside bootp */ + + check_pkt(p, 278, magic_cookie, sizeof(magic_cookie)); + + /* Check dchp message type, can be at different positions */ + check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt)); + break; + } + } + break; + + default: + break; + } + + return ERR_OK; +} + +/* + * Test basic happy flow DHCP session. + * Validate that xid is checked. + */ +START_TEST(test_dhcp) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + int i; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1, "TX %d packets, expected 1", txpacket); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(txpacket == 2, "TX %d packets, expected 2", txpacket); /* DHCP request sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_ack[46], &xid, 4); + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 2, "TX %d packets, still expected 2", txpacket); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + for (i = 0; i < 20; i++) { + tick_lwip(); + } + fail_unless(txpacket == 4, "TX %d packets, expected 4", txpacket); /* ARP requests sent */ + + /* Interface up */ + fail_unless(netif_is_up(&net_test)); + + /* Now it should have taken the IP */ + IP4_ADDR(&addr, 195, 170, 189, 200); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 195, 170, 189, 171); + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + netif_remove(&net_test); +} +END_TEST + +/* + * Test that IP address is not taken and NAK is sent if someone + * replies to ARP requests for the offered address. + */ +START_TEST(test_dhcp_nak) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(txpacket == 2); /* DHCP request sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_ack[46], &xid, 4); + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 2); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack)); + + fail_unless(txpacket == 3); /* ARP request sent */ + + tcase = TEST_LWIP_DHCP_NAK; /* Switch testcase */ + + /* Send arp reply, mark offered IP as taken */ + send_pkt(&net_test, arpreply, sizeof(arpreply)); + + fail_unless(txpacket == 4); /* DHCP nak sent */ + + netif_remove(&net_test); +} +END_TEST + +/* + * Test case based on captured data where + * replies are sent from a different IP than the + * one the client unicasted to. + */ +START_TEST(test_dhcp_relayed) +{ + const u8_t relay_offer[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, + 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfd, 0x53, 0x00, 0x00, 0x40, 0x11, + 0x78, 0x46, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x51, 0x35, + 0xb6, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x49, 0x35, 0x01, 0x02, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff + }; + + const u8_t relay_ack1[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x22, + 0x93, 0x5a, 0xf7, 0x60, 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfd, 0x55, 0x00, 0x00, 0x40, 0x11, + 0x78, 0x44, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x51, 0x35, + 0xb6, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x49, 0x35, 0x01, 0x05, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff + }; + + const u8_t relay_ack2[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, + 0x08, 0x00, 0x45, 0x00, + 0x01, 0x38, 0xfa, 0x18, 0x00, 0x00, 0x40, 0x11, + 0x7b, 0x81, 0x4f, 0x8a, 0x32, 0x02, 0x4f, 0x8a, + 0x33, 0x05, 0x00, 0x43, 0x00, 0x44, 0x01, 0x24, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x49, 0x8b, + 0x6e, 0xab, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x8a, + 0x33, 0x05, 0x4f, 0x8a, 0x33, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0xb5, 0x04, 0x01, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x01, 0x04, 0xff, 0xff, 0xfe, 0x00, + 0x03, 0x04, 0x4f, 0x8a, 0x32, 0x01, 0x06, 0x08, + 0x4f, 0x8a, 0x00, 0xb4, 0x55, 0x08, 0x1f, 0xd1, + 0x1c, 0x04, 0x4f, 0x8a, 0x33, 0xff, 0x33, 0x04, + 0x00, 0x00, 0x54, 0x60, 0x35, 0x01, 0x05, 0x36, + 0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff }; + + const u8_t arp_resp[] = { + 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* DEST */ + 0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60, /* SRC */ + 0x08, 0x06, /* Type: ARP */ + 0x00, 0x01, /* HW: Ethernet */ + 0x08, 0x00, /* PROTO: IP */ + 0x06, /* HW size */ + 0x04, /* PROTO size */ + 0x00, 0x02, /* OPCODE: Reply */ + + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, /* Target MAC */ + 0x4f, 0x8a, 0x32, 0x01, /* Target IP */ + + 0x00, 0x23, 0xc1, 0x00, 0x06, 0x50, /* src mac */ + 0x4f, 0x8a, 0x33, 0x05, /* src ip */ + + /* Padding follows.. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + int i; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP_RELAY; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&relay_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, relay_offer, sizeof(relay_offer)); + + /* request sent? */ + fail_unless(txpacket == 2, "txpkt = %d, should be 2", txpacket); + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&relay_ack1[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, relay_ack1, sizeof(relay_ack1)); + + for (i = 0; i < 25; i++) { + tick_lwip(); + } + fail_unless(txpacket == 4, "txpkt should be 5, is %d", txpacket); /* ARP requests sent */ + + /* Interface up */ + fail_unless(netif_is_up(&net_test)); + + /* Now it should have taken the IP */ + IP4_ADDR(&addr, 79, 138, 51, 5); + IP4_ADDR(&netmask, 255, 255, 254, 0); + IP4_ADDR(&gw, 79, 138, 50, 1); + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 4, "txpacket = %d", txpacket); + + for (i = 0; i < 108000 - 25; i++) { + tick_lwip(); + } + + fail_unless(netif_is_up(&net_test)); + fail_unless(txpacket == 6, "txpacket = %d", txpacket); + + /* We need to send arp response here.. */ + + send_pkt(&net_test, arp_resp, sizeof(arp_resp)); + + fail_unless(txpacket == 7, "txpacket = %d", txpacket); + fail_unless(netif_is_up(&net_test)); + + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&relay_ack2[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, relay_ack2, sizeof(relay_ack2)); + + for (i = 0; i < 100000; i++) { + tick_lwip(); + } + + fail_unless(txpacket == 7, "txpacket = %d", txpacket); + + netif_remove(&net_test); + +} +END_TEST + +START_TEST(test_dhcp_nak_no_endmarker) +{ + struct ip_addr addr; + struct ip_addr netmask; + struct ip_addr gw; + + u8_t dhcp_nack_no_endmarker[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0x75, + 0xd0, 0x26, 0xd0, 0x0d, 0x08, 0x00, 0x45, 0x00, + 0x01, 0x15, 0x38, 0x86, 0x00, 0x00, 0xff, 0x11, + 0xc0, 0xa8, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x43, 0x00, 0x44, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, 0x7a, 0xcb, + 0xba, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, + 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, + 0x53, 0x63, 0x35, 0x01, 0x06, 0x36, 0x04, 0xc0, + 0xa8, 0x01, 0x01, 0x31, 0xef, 0xad, 0x72, 0x31, + 0x43, 0x4e, 0x44, 0x30, 0x32, 0x35, 0x30, 0x43, + 0x52, 0x47, 0x44, 0x38, 0x35, 0x36, 0x3c, 0x08, + 0x4d, 0x53, 0x46, 0x54, 0x20, 0x35, 0x2e, 0x30, + 0x37, 0x0d, 0x01, 0x0f, 0x03, 0x06, 0x2c, 0x2e, + 0x2f, 0x1f, 0x21, 0x79, 0xf9, 0x2b, 0xfc, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x71, + 0xf3, 0x5b, 0xe2, 0x71, 0x2e, 0x01, 0x08, 0x03, + 0x04, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0xeb, 0x1e, + 0x44, 0xec, 0xeb, 0x1e, 0x30, 0x37, 0x0c, 0x01, + 0x0f, 0x03, 0x06, 0x2c, 0x2e, 0x2f, 0x1f, 0x21, + 0x79, 0xf9, 0x2b, 0xff, 0x25, 0xc0, 0x09, 0xd6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + u32_t xid; + LWIP_UNUSED_ARG(_i); + + tcase = TEST_LWIP_DHCP_NAK_NO_ENDMARKER; + setdebug(0); + + IP4_ADDR(&addr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input); + + dhcp_start(&net_test); + + fail_unless(txpacket == 1); /* DHCP discover sent */ + xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */ + memcpy(&dhcp_offer[46], &xid, 4); + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + /* Interface down */ + fail_if(netif_is_up(&net_test)); + + /* IP addresses should be zero */ + fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr))); + fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr))); + fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr))); + + fail_unless(txpacket == 1); /* Nothing more sent */ + xid = htonl(net_test.dhcp->xid); + memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */ + send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer)); + + fail_unless(net_test.dhcp->state == DHCP_REQUESTING); + + fail_unless(txpacket == 2); /* No more sent */ + xid = htonl(net_test.dhcp->xid); /* xid updated */ + memcpy(&dhcp_nack_no_endmarker[46], &xid, 4); /* insert transaction id */ + send_pkt(&net_test, dhcp_nack_no_endmarker, sizeof(dhcp_nack_no_endmarker)); + + /* NAK should put us in another state for a while, no other way detecting it */ + fail_unless(net_test.dhcp->state != DHCP_REQUESTING); + + netif_remove(&net_test); +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +dhcp_suite(void) +{ + TFun tests[] = { + test_dhcp, + test_dhcp_nak, + test_dhcp_relayed, + test_dhcp_nak_no_endmarker + }; + return create_suite("DHCP", tests, sizeof(tests)/sizeof(TFun), dhcp_setup, dhcp_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.h b/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.h new file mode 100644 index 00000000..aff44b70 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/dhcp/test_dhcp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_DHCP_H__ +#define __TEST_DHCP_H__ + +#include "../lwip_check.h" + +Suite* dhcp_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.c b/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.c new file mode 100644 index 00000000..cbbc9502 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.c @@ -0,0 +1,262 @@ +#include "test_etharp.h" + +#include "lwip/udp.h" +#include "netif/etharp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS +#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled" +#endif +#if !ETHARP_SUPPORT_STATIC_ENTRIES +#error "This test needs ETHARP_SUPPORT_STATIC_ENTRIES enabled" +#endif + +static struct netif test_netif; +static ip_addr_t test_ipaddr, test_netmask, test_gw; +struct eth_addr test_ethaddr = {1,1,1,1,1,1}; +struct eth_addr test_ethaddr2 = {1,1,1,1,1,2}; +struct eth_addr test_ethaddr3 = {1,1,1,1,1,3}; +struct eth_addr test_ethaddr4 = {1,1,1,1,1,4}; +static int linkoutput_ctr; + +/* Helper functions */ +static void +etharp_remove_all(void) +{ + int i; + /* call etharp_tmr often enough to have all entries cleaned */ + for(i = 0; i < 0xff; i++) { + etharp_tmr(); + } +} + +static err_t +default_netif_linkoutput(struct netif *netif, struct pbuf *p) +{ + fail_unless(netif == &test_netif); + fail_unless(p != NULL); + linkoutput_ctr++; + return ERR_OK; +} + +static err_t +default_netif_init(struct netif *netif) +{ + fail_unless(netif != NULL); + netif->linkoutput = default_netif_linkoutput; + netif->output = etharp_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + netif->hwaddr_len = ETHARP_HWADDR_LEN; + return ERR_OK; +} + +static void +default_netif_add(void) +{ + IP4_ADDR(&test_gw, 192,168,0,1); + IP4_ADDR(&test_ipaddr, 192,168,0,1); + IP4_ADDR(&test_netmask, 255,255,0,0); + + fail_unless(netif_default == NULL); + netif_set_default(netif_add(&test_netif, &test_ipaddr, &test_netmask, + &test_gw, NULL, default_netif_init, NULL)); + netif_set_up(&test_netif); +} + +static void +default_netif_remove(void) +{ + fail_unless(netif_default == &test_netif); + netif_remove(&test_netif); +} + +static void +create_arp_response(ip_addr_t *adr) +{ + int k; + struct eth_hdr *ethhdr; + struct etharp_hdr *etharphdr; + struct pbuf *p = pbuf_alloc(PBUF_RAW, sizeof(struct eth_hdr) + sizeof(struct etharp_hdr), PBUF_RAM); + if(p == NULL) { + FAIL_RET(); + } + ethhdr = (struct eth_hdr*)p->payload; + etharphdr = (struct etharp_hdr*)(ethhdr + 1); + + ethhdr->dest = test_ethaddr; + ethhdr->src = test_ethaddr2; + ethhdr->type = htons(ETHTYPE_ARP); + + etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1); + etharphdr->proto = htons(ETHTYPE_IP); + etharphdr->hwlen = ETHARP_HWADDR_LEN; + etharphdr->protolen = sizeof(ip_addr_t); + etharphdr->opcode = htons(ARP_REPLY); + + SMEMCPY(ðarphdr->sipaddr, adr, sizeof(ip_addr_t)); + SMEMCPY(ðarphdr->dipaddr, &test_ipaddr, sizeof(ip_addr_t)); + + k = 6; + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + etharphdr->shwaddr.addr[k] = test_ethaddr2.addr[k]; + etharphdr->dhwaddr.addr[k] = test_ethaddr.addr[k]; + /* Write the Ethernet MAC-Addresses */ + ethhdr->dest.addr[k] = test_ethaddr.addr[k]; + ethhdr->src.addr[k] = test_ethaddr2.addr[k]; + } + + ethernet_input(p, &test_netif); +} + +/* Setups/teardown functions */ + +static void +etharp_setup(void) +{ + etharp_remove_all(); + default_netif_add(); +} + +static void +etharp_teardown(void) +{ + etharp_remove_all(); + default_netif_remove(); +} + + +/* Test functions */ + +START_TEST(test_etharp_table) +{ +#if ETHARP_SUPPORT_STATIC_ENTRIES + err_t err; +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + s8_t idx; + ip_addr_t *unused_ipaddr; + struct eth_addr *unused_ethaddr; + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + if (netif_default != &test_netif) { + fail("This test needs a default netif"); + } + + linkoutput_ctr = 0; + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + ip_addr_t adrs[ARP_TABLE_SIZE + 2]; + int i; + for(i = 0; i < ARP_TABLE_SIZE + 2; i++) { + IP4_ADDR(&adrs[i], 192,168,0,i+2); + } + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == i); + etharp_tmr(); + } + } + linkoutput_ctr = 0; +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create one static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE], &test_ethaddr3); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + fail_unless(linkoutput_ctr == 0); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + linkoutput_ctr = 0; + /* fill ARP-table with dynamic entries */ + for(i = 0; i < ARP_TABLE_SIZE; i++) { + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM); + fail_unless(p != NULL); + if (p != NULL) { + err_t err = udp_sendto(pcb, p, &adrs[i], 123); + fail_unless(err == ERR_OK); + /* etharp request sent? */ + fail_unless(linkoutput_ctr == (2*i) + 1); + pbuf_free(p); + + /* create an ARP response */ + create_arp_response(&adrs[i]); + /* queued UDP packet sent? */ + fail_unless(linkoutput_ctr == (2*i) + 2); + + idx = etharp_find_addr(NULL, &adrs[i], &unused_ethaddr, &unused_ipaddr); + if (i < ARP_TABLE_SIZE - 1) { + fail_unless(idx == i+1); + } else { + /* the last entry must not overwrite the static entry! */ + fail_unless(idx == 1); + } + etharp_tmr(); + } + } +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* create a second static entry */ + err = etharp_add_static_entry(&adrs[ARP_TABLE_SIZE+1], &test_ethaddr4); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 2); + /* and remove it again */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE+1]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + /* check that static entries don't time out */ + etharp_remove_all(); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == 0); + +#if ETHARP_SUPPORT_STATIC_ENTRIES + /* remove the first static entry */ + err = etharp_remove_static_entry(&adrs[ARP_TABLE_SIZE]); + fail_unless(err == ERR_OK); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); + idx = etharp_find_addr(NULL, &adrs[ARP_TABLE_SIZE+1], &unused_ethaddr, &unused_ipaddr); + fail_unless(idx == -1); +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + + udp_remove(pcb); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +etharp_suite(void) +{ + TFun tests[] = { + test_etharp_table + }; + return create_suite("ETHARP", tests, sizeof(tests)/sizeof(TFun), etharp_setup, etharp_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.h b/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.h new file mode 100644 index 00000000..96e00c3b --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/etharp/test_etharp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_ETHARP_H__ +#define __TEST_ETHARP_H__ + +#include "../lwip_check.h" + +Suite* etharp_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/lwip_check.h b/external/badvpn_dns/lwip/test/unit/lwip_check.h new file mode 100644 index 00000000..e27f55ae --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/lwip_check.h @@ -0,0 +1,37 @@ +#ifndef __LWIP_CHECK_H__ +#define __LWIP_CHECK_H__ + +/* Common header file for lwIP unit tests using the check framework */ + +#include +#include +#include + +#define FAIL_RET() do { fail(); return; } while(0) +#define EXPECT(x) fail_unless(x) +#define EXPECT_RET(x) do { fail_unless(x); if(!(x)) { return; }} while(0) +#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0) +#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL) + +/** typedef for a function returning a test suite */ +typedef Suite* (suite_getter_fn)(void); + +/** Create a test suite */ +static Suite* create_suite(const char* name, TFun *tests, size_t num_tests, SFun setup, SFun teardown) +{ + size_t i; + Suite *s = suite_create(name); + + for(i = 0; i < num_tests; i++) { + /* Core test case */ + TCase *tc_core = tcase_create("Core"); + if ((setup != NULL) || (teardown != NULL)) { + tcase_add_checked_fixture(tc_core, setup, teardown); + } + tcase_add_test(tc_core, tests[i]); + suite_add_tcase(s, tc_core); + } + return s; +} + +#endif /* __LWIP_CHECK_H__ */ diff --git a/external/badvpn_dns/lwip/test/unit/lwip_unittests.c b/external/badvpn_dns/lwip/test/unit/lwip_unittests.c new file mode 100644 index 00000000..41d1f361 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/lwip_unittests.c @@ -0,0 +1,49 @@ +#include "lwip_check.h" + +#include "udp/test_udp.h" +#include "tcp/test_tcp.h" +#include "tcp/test_tcp_oos.h" +#include "core/test_mem.h" +#include "core/test_pbuf.h" +#include "etharp/test_etharp.h" +#include "dhcp/test_dhcp.h" + +#include "lwip/init.h" + + +int main() +{ + int number_failed; + SRunner *sr; + size_t i; + suite_getter_fn* suites[] = { + udp_suite, + tcp_suite, + tcp_oos_suite, + mem_suite, + pbuf_suite, + etharp_suite, + dhcp_suite + }; + size_t num = sizeof(suites)/sizeof(void*); + LWIP_ASSERT("No suites defined", num > 0); + + lwip_init(); + + sr = srunner_create((suites[0])()); + for(i = 1; i < num; i++) { + srunner_add_suite(sr, ((suite_getter_fn*)suites[i])()); + } + +#ifdef LWIP_UNITTESTS_NOFORK + srunner_set_fork_status(sr, CK_NOFORK); +#endif +#ifdef LWIP_UNITTESTS_FORK + srunner_set_fork_status(sr, CK_FORK); +#endif + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/external/badvpn_dns/lwip/test/unit/lwipopts.h b/external/badvpn_dns/lwip/test/unit/lwipopts.h new file mode 100644 index 00000000..00595154 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/lwipopts.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 + +/* Enable DHCP to test it, disable UDP checksum to easier inject packets */ +#define LWIP_DHCP 1 + +/* Minimal changes to opt.h required for tcp unit tests: */ +#define MEM_SIZE 16000 +#define TCP_SND_QUEUELEN 40 +#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN +#define TCP_SND_BUF (12 * TCP_MSS) +#define TCP_WND (10 * TCP_MSS) + +/* Minimal changes to opt.h required for etharp unit tests: */ +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#endif /* __LWIPOPTS_H__ */ diff --git a/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.c b/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.c new file mode 100644 index 00000000..ff503db3 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.c @@ -0,0 +1,303 @@ +#include "tcp_helper.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "lwip/pbuf.h" +#include "lwip/inet_chksum.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif + +/** Remove all pcbs on the given list. */ +static void +tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + +/** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */ +void +tcp_remove_all(void) +{ + tcp_remove(tcp_listen_pcbs.pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB_LISTEN].used == 0); + fail_unless(lwip_stats.memp[MEMP_TCP_SEG].used == 0); + fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0); +} + +/** Create a TCP segment usable for passing to tcp_input */ +static struct pbuf* +tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags, u16_t wnd) +{ + struct pbuf *p, *q; + struct ip_hdr* iphdr; + struct tcp_hdr* tcphdr; + u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len); + + p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL); + EXPECT_RETNULL(p != NULL); + /* first pbuf must be big enough to hold the headers */ + EXPECT_RETNULL(p->len >= (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + if (data_len > 0) { + /* first pbuf must be big enough to hold at least 1 data byte, too */ + EXPECT_RETNULL(p->len > (sizeof(struct ip_hdr) + sizeof(struct tcp_hdr))); + } + + for(q = p; q != NULL; q = q->next) { + memset(q->payload, 0, q->len); + } + + iphdr = p->payload; + /* fill IP header */ + iphdr->dest.addr = dst_ip->addr; + iphdr->src.addr = src_ip->addr; + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* let p point to TCP header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcphdr = p->payload; + tcphdr->src = htons(src_port); + tcphdr->dest = htons(dst_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_HDRLEN_SET(tcphdr, sizeof(struct tcp_hdr)/4); + TCPH_FLAGS_SET(tcphdr, headerflags); + tcphdr->wnd = htons(wnd); + + if (data_len > 0) { + /* let p point to TCP data */ + pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr)); + /* copy data */ + pbuf_take(p, data, data_len); + /* let p point to TCP header again */ + pbuf_header(p, sizeof(struct tcp_hdr)); + } + + /* calculate checksum */ + + tcphdr->chksum = inet_chksum_pseudo(p, + IP_PROTO_TCP, p->tot_len, src_ip, dst_ip); + + pbuf_header(p, sizeof(struct ip_hdr)); + + return p; +} + +/** Create a TCP segment usable for passing to tcp_input */ +struct pbuf* +tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags) +{ + return tcp_create_segment_wnd(src_ip, dst_ip, src_port, dst_port, data, + data_len, seqno, ackno, headerflags, TCP_WND); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + */ +struct pbuf* +tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, u32_t seqno_offset, + u32_t ackno_offset, u8_t headerflags) +{ + return tcp_create_segment(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port, + data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags); +} + +/** Create a TCP segment usable for passing to tcp_input + * - IP-addresses, ports, seqno and ackno are taken from pcb + * - seqno and ackno can be altered with an offset + * - TCP window can be adjusted + */ +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd) +{ + return tcp_create_segment_wnd(&pcb->remote_ip, &pcb->local_ip, pcb->remote_port, pcb->local_port, + data, data_len, pcb->rcv_nxt + seqno_offset, pcb->lastack + ackno_offset, headerflags, wnd); +} + +/** Safely bring a tcp_pcb into the requested state */ +void +tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port) +{ + /* @todo: are these all states? */ + /* @todo: remove from previous list */ + pcb->state = state; + if (state == ESTABLISHED) { + TCP_REG(&tcp_active_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else if(state == LISTEN) { + TCP_REG(&tcp_listen_pcbs.pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + } else if(state == TIME_WAIT) { + TCP_REG(&tcp_tw_pcbs, pcb); + pcb->local_ip.addr = local_ip->addr; + pcb->local_port = local_port; + pcb->remote_ip.addr = remote_ip->addr; + pcb->remote_port = remote_port; + } else { + fail(); + } +} + +void +test_tcp_counters_err(void* arg, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RET(arg != NULL); + counters->err_calls++; + counters->last_err = err; +} + +static void +test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf* p) +{ + struct pbuf* q; + u32_t i, received; + if(counters->expected_data == NULL) { + /* no data to compare */ + return; + } + EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len); + received = counters->recved_bytes; + for(q = p; q != NULL; q = q->next) { + char *data = q->payload; + for(i = 0; i < q->len; i++) { + EXPECT_RET(data[i] == counters->expected_data[received]); + received++; + } + } + EXPECT(received == counters->recved_bytes + p->tot_len); +} + +err_t +test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err) +{ + struct test_tcp_counters* counters = arg; + EXPECT_RETX(arg != NULL, ERR_OK); + EXPECT_RETX(pcb != NULL, ERR_OK); + EXPECT_RETX(err == ERR_OK, ERR_OK); + + if (p != NULL) { + if (counters->close_calls == 0) { + counters->recv_calls++; + test_tcp_counters_check_rxdata(counters, p); + counters->recved_bytes += p->tot_len; + } else { + counters->recv_calls_after_close++; + counters->recved_bytes_after_close += p->tot_len; + } + pbuf_free(p); + } else { + counters->close_calls++; + } + EXPECT(counters->recv_calls_after_close == 0 && counters->recved_bytes_after_close == 0); + return ERR_OK; +} + +/** Allocate a pcb and set up the test_tcp_counters_* callbacks */ +struct tcp_pcb* +test_tcp_new_counters_pcb(struct test_tcp_counters* counters) +{ + struct tcp_pcb* pcb = tcp_new(); + if (pcb != NULL) { + /* set up args and callbacks */ + tcp_arg(pcb, counters); + tcp_recv(pcb, test_tcp_counters_recv); + tcp_err(pcb, test_tcp_counters_err); + pcb->snd_wnd = TCP_WND; + pcb->snd_wnd_max = TCP_WND; + } + return pcb; +} + +/** Calls tcp_input() after adjusting current_iphdr_dest */ +void test_tcp_input(struct pbuf *p, struct netif *inp) +{ + struct ip_hdr *iphdr = (struct ip_hdr*)p->payload; + /* these lines are a hack, don't use them as an example :-) */ + ip_addr_copy(*ipX_current_dest_addr(), iphdr->dest); + ip_addr_copy(*ipX_current_src_addr(), iphdr->src); + ip_current_netif() = inp; + ip_current_header() = iphdr; + + /* since adding IPv6, p->payload must point to tcp header, not ip header */ + pbuf_header(p, -(s16_t)sizeof(struct ip_hdr)); + + tcp_input(p, inp); + + ipX_current_dest_addr()->addr = 0; + ipX_current_src_addr()->addr = 0; + ip_current_netif() = NULL; + ip_current_header() = NULL; +} + +static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p, + ip_addr_t *ipaddr) +{ + struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state; + LWIP_UNUSED_ARG(ipaddr); + if (txcounters != NULL) + { + txcounters->num_tx_calls++; + txcounters->num_tx_bytes += p->tot_len; + if (txcounters->copy_tx_packets) { + struct pbuf *p_copy = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + err_t err; + EXPECT(p_copy != NULL); + err = pbuf_copy(p_copy, p); + EXPECT(err == ERR_OK); + if (txcounters->tx_packets == NULL) { + txcounters->tx_packets = p_copy; + } else { + pbuf_cat(txcounters->tx_packets, p_copy); + } + } + } + return ERR_OK; +} + +void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters, + ip_addr_t *ip_addr, ip_addr_t *netmask) +{ + struct netif *n; + memset(netif, 0, sizeof(struct netif)); + if (txcounters != NULL) { + memset(txcounters, 0, sizeof(struct test_tcp_txcounters)); + netif->state = txcounters; + } + netif->output = test_tcp_netif_output; + netif->flags |= NETIF_FLAG_UP; + ip_addr_copy(netif->netmask, *netmask); + ip_addr_copy(netif->ip_addr, *ip_addr); + for (n = netif_list; n != NULL; n = n->next) { + if (n == netif) { + return; + } + } + netif->next = NULL; + netif_list = netif; +} diff --git a/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.h b/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.h new file mode 100644 index 00000000..4a72c935 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/tcp_helper.h @@ -0,0 +1,52 @@ +#ifndef __TCP_HELPER_H__ +#define __TCP_HELPER_H__ + +#include "../lwip_check.h" +#include "lwip/arch.h" +#include "lwip/tcp.h" +#include "lwip/netif.h" + +/* counters used for test_tcp_counters_* callback functions */ +struct test_tcp_counters { + u32_t recv_calls; + u32_t recved_bytes; + u32_t recv_calls_after_close; + u32_t recved_bytes_after_close; + u32_t close_calls; + u32_t err_calls; + err_t last_err; + char* expected_data; + u32_t expected_data_len; +}; + +struct test_tcp_txcounters { + u32_t num_tx_calls; + u32_t num_tx_bytes; + u8_t copy_tx_packets; + struct pbuf *tx_packets; +}; + +/* Helper functions */ +void tcp_remove_all(void); + +struct pbuf* tcp_create_segment(ip_addr_t* src_ip, ip_addr_t* dst_ip, + u16_t src_port, u16_t dst_port, void* data, size_t data_len, + u32_t seqno, u32_t ackno, u8_t headerflags); +struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags); +struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len, + u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd); +void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip, + ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port); +void test_tcp_counters_err(void* arg, err_t err); +err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err); + +struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters); + +void test_tcp_input(struct pbuf *p, struct netif *inp); + +void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters, + ip_addr_t *ip_addr, ip_addr_t *netmask); + + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.c b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.c new file mode 100644 index 00000000..81720984 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.c @@ -0,0 +1,671 @@ +#include "test_tcp.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */ +#endif + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif +#if TCP_SND_BUF <= TCP_WND +#error "This tests needs TCP_SND_BUF to be > TCP_WND" +#endif + +static u8_t test_tcp_timer; + +/* our own version of tcp_tmr so we can reset fast/slow timer state */ +static void +test_tcp_tmr(void) +{ + tcp_fasttmr(); + if (++test_tcp_timer & 1) { + tcp_slowtmr(); + } +} + +/* Setups/teardown functions */ + +static void +tcp_setup(void) +{ + /* reset iss to default (6510) */ + tcp_ticks = 0; + tcp_ticks = 0 - (tcp_next_iss() - 6510); + tcp_next_iss(); + tcp_ticks = 0; + + test_tcp_timer = 0; + tcp_remove_all(); +} + +static void +tcp_teardown(void) +{ + tcp_remove_all(); + netif_list = NULL; + netif_default = NULL; +} + + +/* Test functions */ + +/** Call tcp_new() and tcp_abort() and test memp stats */ +START_TEST(test_tcp_new_abort) +{ + struct tcp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + + pcb = tcp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0); + } +} +END_TEST + +/** Create an ESTABLISHED pcb and check if receive callback is called */ +START_TEST(test_tcp_recv_inseq) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + char data[] = {1, 2, 3, 4}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + struct test_tcp_txcounters txcounters; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create a segment */ + p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); + EXPECT(p != NULL); + if (p != NULL) { + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. + * At the end, send more data. */ +START_TEST(test_tcp_fast_retx_recover) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + char data1[] = { 1, 2, 3, 4}; + char data2[] = { 5, 6, 7, 8}; + char data3[] = { 9, 10, 11, 12}; + char data4[] = {13, 14, 15, 16}; + char data5[] = {17, 18, 19, 20}; + char data6[] = {21, 22, 23, 24}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = pcb->snd_wnd; + + /* send data1 */ + err = tcp_write(pcb, data1, sizeof(data1), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(txcounters.num_tx_calls == 1); + EXPECT_RET(txcounters.num_tx_bytes == sizeof(data1) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); + memset(&txcounters, 0, sizeof(txcounters)); + /* "recv" ACK for data1 */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 4, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->unacked == NULL); + /* send data2 */ + err = tcp_write(pcb, data2, sizeof(data2), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT_RET(txcounters.num_tx_calls == 1); + EXPECT_RET(txcounters.num_tx_bytes == sizeof(data2) + sizeof(struct tcp_hdr) + sizeof(struct ip_hdr)); + memset(&txcounters, 0, sizeof(txcounters)); + /* duplicate ACK for data1 (data2 is lost) */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->dupacks == 1); + /* send data3 */ + err = tcp_write(pcb, data3, sizeof(data3), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* nagle enabled, no tx calls */ + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + /* 2nd duplicate ACK for data1 (data2 and data3 are lost) */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(pcb->dupacks == 2); + /* queue data4, don't send it (unsent-oversize is != 0) */ + err = tcp_write(pcb, data4, sizeof(data4), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + /* 3nd duplicate ACK for data1 (data2 and data3 are lost) -> fast retransmission */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + /*EXPECT_RET(txcounters.num_tx_calls == 1);*/ + EXPECT_RET(pcb->dupacks == 3); + memset(&txcounters, 0, sizeof(txcounters)); + /* TODO: check expected data?*/ + + /* send data5, not output yet */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + /*err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK);*/ + EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + { + int i = 0; + do + { + err = tcp_write(pcb, data6, TCP_MSS, TCP_WRITE_FLAG_COPY); + i++; + }while(err == ERR_OK); + EXPECT_RET(err != ERR_OK); + } + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /*EXPECT_RET(txcounters.num_tx_calls == 0); + EXPECT_RET(txcounters.num_tx_bytes == 0);*/ + memset(&txcounters, 0, sizeof(txcounters)); + + /* send even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + + /* send ACKs for data2 and data3 */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 12, TCP_ACK); + EXPECT_RET(p != NULL); + test_tcp_input(p, &netif); + /*EXPECT_RET(txcounters.num_tx_calls == 0);*/ + + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + /* ...and even more data */ + err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + +#if 0 + /* create expected segment */ + p1 = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0); + EXPECT_RET(p != NULL); + if (p != NULL) { + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT_RET(counters.close_calls == 0); + EXPECT_RET(counters.recv_calls == 1); + EXPECT_RET(counters.recved_bytes == data_len); + EXPECT_RET(counters.err_calls == 0); + } +#endif + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +static u8_t tx_data[TCP_WND*2]; + +static void +check_seqnos(struct tcp_seg *segs, int num_expected, u32_t *seqnos_expected) +{ + struct tcp_seg *s = segs; + int i; + for (i = 0; i < num_expected; i++, s = s->next) { + EXPECT_RET(s != NULL); + EXPECT(s->tcphdr->seqno == htonl(seqnos_expected[i])); + } + EXPECT(s == NULL); +} + +/** Send data with sequence numbers that wrap around the u32_t range. + * Then, provoke fast retransmission by duplicate ACKs and check that all + * segment lists are still properly sorted. */ +START_TEST(test_tcp_fast_rexmit_wraparound) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf* p; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)i; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + tcp_ticks = SEQNO1 - ISS; + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = 2*TCP_MSS; + + /* send 6 mss-sized segments */ + for (i = 0; i < 6; i++) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + sent_total += TCP_MSS; + } + check_seqnos(pcb->unsent, 6, seqnos); + EXPECT(pcb->unacked == NULL); + err = tcp_output(pcb); + EXPECT(txcounters.num_tx_calls == 2); + EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); + memset(&txcounters, 0, sizeof(txcounters)); + + check_seqnos(pcb->unacked, 2, seqnos); + check_seqnos(pcb->unsent, 4, &seqnos[2]); + + /* ACK the first segment */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK); + test_tcp_input(p, &netif); + /* ensure this didn't trigger a retransmission */ + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + check_seqnos(pcb->unacked, 2, &seqnos[1]); + check_seqnos(pcb->unsent, 3, &seqnos[3]); + + /* 3 dupacks */ + EXPECT(pcb->dupacks == 0); + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(pcb->dupacks == 1); + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(pcb->dupacks == 2); + /* 3rd dupack -> fast rexmit */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + EXPECT(pcb->dupacks == 3); + EXPECT(txcounters.num_tx_calls == 4); + memset(&txcounters, 0, sizeof(txcounters)); + EXPECT(pcb->unsent == NULL); + check_seqnos(pcb->unacked, 5, &seqnos[1]); + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Send data with sequence numbers that wrap around the u32_t range. + * Then, provoke RTO retransmission and check that all + * segment lists are still properly sorted. */ +START_TEST(test_tcp_rto_rexmit_wraparound) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; +#define SEQNO1 (0xFFFFFF00 - TCP_MSS) +#define ISS 6510 + u16_t i, sent_total = 0; + u32_t seqnos[] = { + SEQNO1, + SEQNO1 + (1 * TCP_MSS), + SEQNO1 + (2 * TCP_MSS), + SEQNO1 + (3 * TCP_MSS), + SEQNO1 + (4 * TCP_MSS), + SEQNO1 + (5 * TCP_MSS)}; + LWIP_UNUSED_ARG(_i); + + for (i = 0; i < sizeof(tx_data); i++) { + tx_data[i] = (u8_t)i; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + + /* create and initialize the pcb */ + tcp_ticks = 0; + tcp_ticks = 0 - tcp_next_iss(); + tcp_ticks = SEQNO1 - tcp_next_iss(); + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + EXPECT(pcb->lastack == SEQNO1); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = 2*TCP_MSS; + + /* send 6 mss-sized segments */ + for (i = 0; i < 6; i++) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + sent_total += TCP_MSS; + } + check_seqnos(pcb->unsent, 6, seqnos); + EXPECT(pcb->unacked == NULL); + err = tcp_output(pcb); + EXPECT(txcounters.num_tx_calls == 2); + EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U)); + memset(&txcounters, 0, sizeof(txcounters)); + + check_seqnos(pcb->unacked, 2, seqnos); + check_seqnos(pcb->unsent, 4, &seqnos[2]); + + /* call the tcp timer some times */ + for (i = 0; i < 10; i++) { + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 0); + } + /* 11th call to tcp_tmr: RTO rexmit fires */ + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 1); + check_seqnos(pcb->unacked, 1, seqnos); + check_seqnos(pcb->unsent, 5, &seqnos[1]); + + /* fake greater cwnd */ + pcb->cwnd = pcb->snd_wnd; + /* send more data */ + err = tcp_output(pcb); + EXPECT(err == ERR_OK); + /* check queues are sorted */ + EXPECT(pcb->unsent == NULL); + check_seqnos(pcb->unacked, 6, seqnos); + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data. + * At the end, send more data. */ +static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent) +{ + struct netif netif; + struct test_tcp_txcounters txcounters; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + err_t err; + u16_t sent_total, i; + u8_t expected = 0xFE; + + for (i = 0; i < sizeof(tx_data); i++) { + u8_t d = (u8_t)i; + if (d == 0xFE) { + d = 0xF0; + } + tx_data[i] = d; + } + if (zero_window_probe_from_unsent) { + tx_data[TCP_WND] = expected; + } else { + tx_data[0] = expected; + } + + /* initialize local vars */ + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask); + memset(&counters, 0, sizeof(counters)); + memset(&txcounters, 0, sizeof(txcounters)); + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->mss = TCP_MSS; + /* disable initial congestion window (we don't send a SYN here...) */ + pcb->cwnd = pcb->snd_wnd; + + /* send a full window (minus 1 packets) of TCP data in MSS-sized chunks */ + sent_total = 0; + if ((TCP_WND - TCP_MSS) % TCP_MSS != 0) { + u16_t initial_data_len = (TCP_WND - TCP_MSS) % TCP_MSS; + err = tcp_write(pcb, &tx_data[sent_total], initial_data_len, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == initial_data_len + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + sent_total += initial_data_len; + } + for (; sent_total < (TCP_WND - TCP_MSS); sent_total += TCP_MSS) { + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + } + EXPECT(sent_total == (TCP_WND - TCP_MSS)); + + /* now ACK the packet before the first */ + p = tcp_create_rx_segment(pcb, NULL, 0, 0, 0, TCP_ACK); + test_tcp_input(p, &netif); + /* ensure this didn't trigger a retransmission */ + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + + EXPECT(pcb->persist_backoff == 0); + /* send the last packet, now a complete window has been sent */ + err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY); + sent_total += TCP_MSS; + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U); + memset(&txcounters, 0, sizeof(txcounters)); + EXPECT(pcb->persist_backoff == 0); + + if (zero_window_probe_from_unsent) { + /* ACK all data but close the TX window */ + p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_WND, TCP_ACK, 0); + test_tcp_input(p, &netif); + /* ensure this didn't trigger any transmission */ + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + EXPECT(pcb->persist_backoff == 1); + } + + /* send one byte more (out of window) -> persist timer starts */ + err = tcp_write(pcb, &tx_data[sent_total], 1, TCP_WRITE_FLAG_COPY); + EXPECT_RET(err == ERR_OK); + err = tcp_output(pcb); + EXPECT_RET(err == ERR_OK); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + memset(&txcounters, 0, sizeof(txcounters)); + if (!zero_window_probe_from_unsent) { + /* no persist timer unless a zero window announcement has been received */ + EXPECT(pcb->persist_backoff == 0); + } else { + EXPECT(pcb->persist_backoff == 1); + + /* call tcp_timer some more times to let persist timer count up */ + for (i = 0; i < 4; i++) { + test_tcp_tmr(); + EXPECT(txcounters.num_tx_calls == 0); + EXPECT(txcounters.num_tx_bytes == 0); + } + + /* this should trigger the zero-window-probe */ + txcounters.copy_tx_packets = 1; + test_tcp_tmr(); + txcounters.copy_tx_packets = 0; + EXPECT(txcounters.num_tx_calls == 1); + EXPECT(txcounters.num_tx_bytes == 1 + 40U); + EXPECT(txcounters.tx_packets != NULL); + if (txcounters.tx_packets != NULL) { + u8_t sent; + u16_t ret; + ret = pbuf_copy_partial(txcounters.tx_packets, &sent, 1, 40U); + EXPECT(ret == 1); + EXPECT(sent == expected); + } + if (txcounters.tx_packets != NULL) { + pbuf_free(txcounters.tx_packets); + txcounters.tx_packets = NULL; + } + } + + /* make sure the pcb is freed */ + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} + +START_TEST(test_tcp_tx_full_window_lost_from_unsent) +{ + LWIP_UNUSED_ARG(_i); + test_tcp_tx_full_window_lost(1); +} +END_TEST + +START_TEST(test_tcp_tx_full_window_lost_from_unacked) +{ + LWIP_UNUSED_ARG(_i); + test_tcp_tx_full_window_lost(0); +} +END_TEST + +/** Create the suite including all tests for this module */ +Suite * +tcp_suite(void) +{ + TFun tests[] = { + test_tcp_new_abort, + test_tcp_recv_inseq, + test_tcp_fast_retx_recover, + test_tcp_fast_rexmit_wraparound, + test_tcp_rto_rexmit_wraparound, + test_tcp_tx_full_window_lost_from_unacked, + test_tcp_tx_full_window_lost_from_unsent + }; + return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.h b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.h new file mode 100644 index 00000000..f1c4a463 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_H__ +#define __TEST_TCP_H__ + +#include "../lwip_check.h" + +Suite *tcp_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.c b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.c new file mode 100644 index 00000000..612c4680 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.c @@ -0,0 +1,958 @@ +#include "test_tcp_oos.h" + +#include "lwip/tcp_impl.h" +#include "lwip/stats.h" +#include "tcp_helper.h" + +#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS +#error "This tests needs TCP- and MEMP-statistics enabled" +#endif +#if !TCP_QUEUE_OOSEQ +#error "This tests needs TCP_QUEUE_OOSEQ enabled" +#endif + +/** CHECK_SEGMENTS_ON_OOSEQ: + * 1: check count, seqno and len of segments on pcb->ooseq (strict) + * 0: only check that bytes are received in correct order (less strict) */ +#define CHECK_SEGMENTS_ON_OOSEQ 1 + +#if CHECK_SEGMENTS_ON_OOSEQ +#define EXPECT_OOSEQ(x) EXPECT(x) +#else +#define EXPECT_OOSEQ(x) +#endif + +/* helper functions */ + +/** Get the numbers of segments on the ooseq list */ +static int tcp_oos_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num++; + seg = seg->next; + } + return num; +} + +/** Get the numbers of pbufs on the ooseq list */ +static int tcp_oos_pbuf_count(struct tcp_pcb* pcb) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + while(seg != NULL) { + num += pbuf_clen(seg->p); + seg = seg->next; + } + return num; +} + +/** Get the seqno of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return seqno of the segment + */ +static u32_t +tcp_oos_seg_seqno(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return seg->tcphdr->seqno; + } + num++; + seg = seg->next; + } + fail(); + return 0; +} + +/** Get the tcplen (datalen + SYN/FIN) of a segment (by index) on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @param seg_index index of the segment on the ooseq list + * @return tcplen of the segment + */ +static int +tcp_oos_seg_tcplen(struct tcp_pcb* pcb, int seg_index) +{ + int num = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + if(num == seg_index) { + return TCP_TCPLEN(seg); + } + num++; + seg = seg->next; + } + fail(); + return -1; +} + +/** Get the tcplen (datalen + SYN/FIN) of all segments on the ooseq list + * + * @param pcb the pcb to check for ooseq segments + * @return tcplen of all segment + */ +static int +tcp_oos_tcplen(struct tcp_pcb* pcb) +{ + int len = 0; + struct tcp_seg* seg = pcb->ooseq; + + /* then check the actual segment */ + while(seg != NULL) { + len += TCP_TCPLEN(seg); + seg = seg->next; + } + return len; +} + +/* Setup/teardown functions */ + +static void +tcp_oos_setup(void) +{ + tcp_remove_all(); +} + +static void +tcp_oos_teardown(void) +{ + tcp_remove_all(); + netif_list = NULL; + netif_default = NULL; +} + + + +/* Test functions */ + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received in out-of-sequence segments only */ +START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_8_9, *p_4_8, *p_4_10, *p_2_14, *p_fin, *pinseq; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p1: 8 bytes before FIN */ + /* seqno: 8..16 */ + p_8_9 = tcp_create_rx_segment(pcb, &data[8], 8, 8, 0, TCP_ACK|TCP_FIN); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer */ + /* seqno: 4..13 */ + p_4_10 = tcp_create_rx_segment(pcb, &data[4], 10, 4, 0, TCP_ACK); + /* p4: 14 bytes before FIN, includes data from p1 and p2, plus partly from pinseq */ + /* seqno: 2..15 */ + p_2_14 = tcp_create_rx_segment(pcb, &data[2], 14, 2, 0, TCP_ACK); + /* FIN, seqno 16 */ + p_fin = tcp_create_rx_segment(pcb, NULL, 0,16, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_8_9 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_4_10 != NULL); + EXPECT(p_2_14 != NULL); + EXPECT(p_fin != NULL); + if ((pinseq != NULL) && (p_8_9 != NULL) && (p_4_8 != NULL) && (p_4_10 != NULL) && (p_2_14 != NULL) && (p_fin != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_8_9, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_10, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 4); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 8); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 9); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_14, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(p_fin, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 15); /* includes FIN */ + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + + +/** create multiple segments and pass them to tcp_input in a wrong + * order to see if ooseq-caching works correctly + * FIN is received IN-SEQUENCE at the end */ +START_TEST(test_tcp_recv_ooseq_FIN_INSEQ) +{ + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_1_2, *p_4_8, *p_3_11, *p_2_12, *p_15_1, *p_15_1a, *pinseq, *pinseqFIN; + char data[] = { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + ip_addr_t remote_ip, local_ip, netmask; + u16_t data_len; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + LWIP_UNUSED_ARG(_i); + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + data_len = sizeof(data); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = data_len; + counters.expected_data = data; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + + /* create segments */ + /* p1: 7 bytes - 2 before FIN */ + /* seqno: 1..2 */ + p_1_2 = tcp_create_rx_segment(pcb, &data[1], 2, 1, 0, TCP_ACK); + /* p2: 4 bytes before p1, including the first 4 bytes of p1 (partly duplicate) */ + /* seqno: 4..11 */ + p_4_8 = tcp_create_rx_segment(pcb, &data[4], 8, 4, 0, TCP_ACK); + /* p3: same as p2 but 2 bytes longer and one byte more at the front */ + /* seqno: 3..13 */ + p_3_11 = tcp_create_rx_segment(pcb, &data[3], 11, 3, 0, TCP_ACK); + /* p4: 13 bytes - 2 before FIN - should be ignored as contained in p1 and p3 */ + /* seqno: 2..13 */ + p_2_12 = tcp_create_rx_segment(pcb, &data[2], 12, 2, 0, TCP_ACK); + /* pinseq is the first segment that is held back to create ooseq! */ + /* seqno: 0..3 */ + pinseq = tcp_create_rx_segment(pcb, &data[0], 4, 0, 0, TCP_ACK); + /* p5: last byte before FIN */ + /* seqno: 15 */ + p_15_1 = tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* p6: same as p5, should be ignored */ + p_15_1a= tcp_create_rx_segment(pcb, &data[15], 1, 15, 0, TCP_ACK); + /* pinseqFIN: last 2 bytes plus FIN */ + /* only segment containing seqno 14 and FIN */ + pinseqFIN = tcp_create_rx_segment(pcb, &data[14], 2, 14, 0, TCP_ACK|TCP_FIN); + EXPECT(pinseq != NULL); + EXPECT(p_1_2 != NULL); + EXPECT(p_4_8 != NULL); + EXPECT(p_3_11 != NULL); + EXPECT(p_2_12 != NULL); + EXPECT(p_15_1 != NULL); + EXPECT(p_15_1a != NULL); + EXPECT(pinseqFIN != NULL); + if ((pinseq != NULL) && (p_1_2 != NULL) && (p_4_8 != NULL) && (p_3_11 != NULL) && (p_2_12 != NULL) + && (p_15_1 != NULL) && (p_15_1a != NULL) && (pinseqFIN != NULL)) { + /* pass the segment to tcp_input */ + test_tcp_input(p_1_2, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + + /* pass the segment to tcp_input */ + test_tcp_input(p_4_8, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 4); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 8); + + /* pass the segment to tcp_input */ + test_tcp_input(p_3_11, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 2); + /* p_3_11 has removed p_4_8 from ooseq */ + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 3); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 11); + + /* pass the segment to tcp_input */ + test_tcp_input(p_2_12, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 2); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 1) == 2); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 1) == 12); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseq, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(p_15_1a, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 1); + EXPECT(counters.recved_bytes == 14); + EXPECT(counters.err_calls == 0); + /* check ooseq queue: unchanged */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == 1); + EXPECT_OOSEQ(tcp_oos_seg_seqno(pcb, 0) == 15); + EXPECT_OOSEQ(tcp_oos_seg_tcplen(pcb, 0) == 1); + + /* pass the segment to tcp_input */ + test_tcp_input(pinseqFIN, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 1); + EXPECT(counters.recv_calls == 2); + EXPECT(counters.recved_bytes == data_len); + EXPECT(counters.err_calls == 0); + EXPECT(pcb->ooseq == NULL); + } + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} +END_TEST + +static char data_full_wnd[TCP_WND]; + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +START_TEST(test_tcp_recv_ooseq_overrun_rxwin) +{ +#if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *pinseq, *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* create segments */ + /* pinseq is sent as last segment! */ + pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + + for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) { + int count, expected_datalen; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], + TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(p != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_count(pcb); + EXPECT_OOSEQ(count == k+1); + datalen = tcp_oos_tcplen(pcb); + if (i + TCP_MSS < TCP_WND) { + expected_datalen = (k+1)*TCP_MSS; + } else { + expected_datalen = TCP_WND - TCP_MSS; + } + if (datalen != expected_datalen) { + EXPECT_OOSEQ(datalen == expected_datalen); + } + } + + /* pass in one more segment, cleary overrunning the rxwin */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == k); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == datalen2); + + /* now pass inseq */ + test_tcp_input(pinseq, &netif); + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_bytes) +{ +#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(k = 1, i = 1; k < TCP_OOSEQ_MAX_BYTES; k += TCP_MSS, i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[k], + TCP_MSS, k, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->next == NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == (i * TCP_MSS)); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[k+1], 1, k+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == ((i-1) * TCP_MSS)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +START_TEST(test_tcp_recv_ooseq_max_pbufs) +{ +#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + int i; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_ovr; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + int datalen = 0; + int datalen2; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */ + + /* create segments and 'recv' them */ + for(i = 1; i <= TCP_OOSEQ_MAX_PBUFS; i++) { + int count; + struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[i], + 1, i, 0, TCP_ACK); + EXPECT_RET(p != NULL); + EXPECT_RET(p->next == NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue */ + count = tcp_oos_pbuf_count(pcb); + EXPECT_OOSEQ(count == i); + datalen = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen == i); + } + + /* pass in one more segment, overrunning the limit */ + p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[i+1], 1, i+1, 0, TCP_ACK); + EXPECT_RET(p_ovr != NULL); + /* pass the segment to tcp_input */ + test_tcp_input(p_ovr, &netif); + /* check if counters are as expected */ + EXPECT(counters.close_calls == 0); + EXPECT(counters.recv_calls == 0); + EXPECT(counters.recved_bytes == 0); + EXPECT(counters.err_calls == 0); + /* check ooseq queue (ensure the new segment was not accepted) */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == (i-1)); + datalen2 = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(datalen2 == (i-1)); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */ + LWIP_UNUSED_ARG(_i); +} +END_TEST + +static void +check_rx_counters(struct tcp_pcb *pcb, struct test_tcp_counters *counters, u32_t exp_close_calls, u32_t exp_rx_calls, + u32_t exp_rx_bytes, u32_t exp_err_calls, int exp_oos_count, int exp_oos_len) +{ + int oos_len; + EXPECT(counters->close_calls == exp_close_calls); + EXPECT(counters->recv_calls == exp_rx_calls); + EXPECT(counters->recved_bytes == exp_rx_bytes); + EXPECT(counters->err_calls == exp_err_calls); + /* check that pbuf is queued in ooseq */ + EXPECT_OOSEQ(tcp_oos_count(pcb) == exp_oos_count); + oos_len = tcp_oos_tcplen(pcb); + EXPECT_OOSEQ(exp_oos_len == oos_len); +} + +/* this test uses 4 packets: + * - data (len=TCP_MSS) + * - FIN + * - data after FIN (len=1) (invalid) + * - 2nd FIN (invalid) + * + * the parameter 'delay_packet' is a bitmask that choses which on these packets is ooseq + */ +static void test_tcp_recv_ooseq_double_FINs(int delay_packet) +{ + int i, k; + struct test_tcp_counters counters; + struct tcp_pcb* pcb; + struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq; + ip_addr_t remote_ip, local_ip, netmask; + u16_t remote_port = 0x100, local_port = 0x101; + struct netif netif; + u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0; + int first_dropped = 0xff; + int last_dropped = 0; + + for(i = 0; i < sizeof(data_full_wnd); i++) { + data_full_wnd[i] = (char)i; + } + + /* initialize local vars */ + memset(&netif, 0, sizeof(netif)); + IP4_ADDR(&local_ip, 192, 168, 1, 1); + IP4_ADDR(&remote_ip, 192, 168, 1, 2); + IP4_ADDR(&netmask, 255, 255, 255, 0); + test_tcp_init_netif(&netif, NULL, &local_ip, &netmask); + /* initialize counter struct */ + memset(&counters, 0, sizeof(counters)); + counters.expected_data_len = TCP_WND; + counters.expected_data = data_full_wnd; + + /* create and initialize the pcb */ + pcb = test_tcp_new_counters_pcb(&counters); + EXPECT_RET(pcb != NULL); + tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port); + pcb->rcv_nxt = 0x8000; + + /* create segments */ + p = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK); + p_normal_fin = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS, 0, TCP_ACK|TCP_FIN); + k = 1; + p_data_after_fin = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS+1], k, TCP_MSS+1, 0, TCP_ACK); + p_2nd_fin_ooseq = tcp_create_rx_segment(pcb, NULL, 0, TCP_MSS+1+k, 0, TCP_ACK|TCP_FIN); + + if(delay_packet & 1) { + /* drop normal data */ + first_dropped = 1; + last_dropped = 1; + } else { + /* send normal data */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* drop FIN */ + if(first_dropped > 2) { + first_dropped = 2; + } + last_dropped = 2; + } else { + /* send FIN */ + test_tcp_input(p_normal_fin, &netif); + if (first_dropped < 2) { + /* already dropped packets, this one is ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } else { + /* inseq */ + exp_close_calls++; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* drop data-after-FIN */ + if(first_dropped > 3) { + first_dropped = 3; + } + last_dropped = 3; + } else { + /* send data-after-FIN */ + test_tcp_input(p_data_after_fin, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen += k; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* drop 2nd-FIN */ + if(first_dropped > 4) { + first_dropped = 4; + } + last_dropped = 4; + } else { + /* send 2nd-FIN */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + if (first_dropped < 3) { + /* already dropped packets, this one is ooseq */ + if (delay_packet & 2) { + /* correct FIN was ooseq */ + exp_oos_pbufs++; + exp_oos_tcplen++; + } + } else { + /* inseq: no change */ + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 1) { + /* dropped normal data before */ + test_tcp_input(p, &netif); + exp_rx_calls++; + exp_rx_bytes += TCP_MSS; + if((delay_packet & 2) == 0) { + /* normal FIN was NOT delayed */ + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 2) { + /* dropped normal FIN before */ + test_tcp_input(p_normal_fin, &netif); + exp_close_calls++; + exp_oos_pbufs = exp_oos_tcplen = 0; + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 4) { + /* dropped data-after-FIN before */ + test_tcp_input(p_data_after_fin, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + if(delay_packet & 8) { + /* dropped 2nd-FIN before */ + test_tcp_input(p_2nd_fin_ooseq, &netif); + } + /* check if counters are as expected */ + check_rx_counters(pcb, &counters, exp_close_calls, exp_rx_calls, exp_rx_bytes, 0, exp_oos_pbufs, exp_oos_tcplen); + + /* check that ooseq data has been dumped */ + EXPECT(pcb->ooseq == NULL); + + /* make sure the pcb is freed */ + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1); + tcp_abort(pcb); + EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0); +} + +/** create multiple segments and pass them to tcp_input with the first segment missing + * to simulate overruning the rxwin with ooseq queueing enabled */ +#define FIN_TEST(name, num) \ + START_TEST(name) \ + { \ + LWIP_UNUSED_ARG(_i); \ + test_tcp_recv_ooseq_double_FINs(num); \ + } \ + END_TEST +FIN_TEST(test_tcp_recv_ooseq_double_FIN_0, 0) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_1, 1) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_2, 2) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_3, 3) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_4, 4) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_5, 5) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_6, 6) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_7, 7) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_8, 8) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_9, 9) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_10, 10) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_11, 11) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_12, 12) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_13, 13) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_14, 14) +FIN_TEST(test_tcp_recv_ooseq_double_FIN_15, 15) + + +/** Create the suite including all tests for this module */ +Suite * +tcp_oos_suite(void) +{ + TFun tests[] = { + test_tcp_recv_ooseq_FIN_OOSEQ, + test_tcp_recv_ooseq_FIN_INSEQ, + test_tcp_recv_ooseq_overrun_rxwin, + test_tcp_recv_ooseq_max_bytes, + test_tcp_recv_ooseq_max_pbufs, + test_tcp_recv_ooseq_double_FIN_0, + test_tcp_recv_ooseq_double_FIN_1, + test_tcp_recv_ooseq_double_FIN_2, + test_tcp_recv_ooseq_double_FIN_3, + test_tcp_recv_ooseq_double_FIN_4, + test_tcp_recv_ooseq_double_FIN_5, + test_tcp_recv_ooseq_double_FIN_6, + test_tcp_recv_ooseq_double_FIN_7, + test_tcp_recv_ooseq_double_FIN_8, + test_tcp_recv_ooseq_double_FIN_9, + test_tcp_recv_ooseq_double_FIN_10, + test_tcp_recv_ooseq_double_FIN_11, + test_tcp_recv_ooseq_double_FIN_12, + test_tcp_recv_ooseq_double_FIN_13, + test_tcp_recv_ooseq_double_FIN_14, + test_tcp_recv_ooseq_double_FIN_15 + }; + return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(TFun), tcp_oos_setup, tcp_oos_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.h b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.h new file mode 100644 index 00000000..5e411f00 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/tcp/test_tcp_oos.h @@ -0,0 +1,8 @@ +#ifndef __TEST_TCP_OOS_H__ +#define __TEST_TCP_OOS_H__ + +#include "../lwip_check.h" + +Suite *tcp_oos_suite(void); + +#endif diff --git a/external/badvpn_dns/lwip/test/unit/udp/test_udp.c b/external/badvpn_dns/lwip/test/unit/udp/test_udp.c new file mode 100644 index 00000000..a2f02af0 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/udp/test_udp.c @@ -0,0 +1,68 @@ +#include "test_udp.h" + +#include "lwip/udp.h" +#include "lwip/stats.h" + +#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS +#error "This tests needs UDP- and MEMP-statistics enabled" +#endif + +/* Helper functions */ +static void +udp_remove_all(void) +{ + struct udp_pcb *pcb = udp_pcbs; + struct udp_pcb *pcb2; + + while(pcb != NULL) { + pcb2 = pcb; + pcb = pcb->next; + udp_remove(pcb2); + } + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); +} + +/* Setups/teardown functions */ + +static void +udp_setup(void) +{ + udp_remove_all(); +} + +static void +udp_teardown(void) +{ + udp_remove_all(); +} + + +/* Test functions */ + +START_TEST(test_udp_new_remove) +{ + struct udp_pcb* pcb; + LWIP_UNUSED_ARG(_i); + + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + + pcb = udp_new(); + fail_unless(pcb != NULL); + if (pcb != NULL) { + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 1); + udp_remove(pcb); + fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0); + } +} +END_TEST + + +/** Create the suite including all tests for this module */ +Suite * +udp_suite(void) +{ + TFun tests[] = { + test_udp_new_remove, + }; + return create_suite("UDP", tests, sizeof(tests)/sizeof(TFun), udp_setup, udp_teardown); +} diff --git a/external/badvpn_dns/lwip/test/unit/udp/test_udp.h b/external/badvpn_dns/lwip/test/unit/udp/test_udp.h new file mode 100644 index 00000000..93353682 --- /dev/null +++ b/external/badvpn_dns/lwip/test/unit/udp/test_udp.h @@ -0,0 +1,8 @@ +#ifndef __TEST_UDP_H__ +#define __TEST_UDP_H__ + +#include "../lwip_check.h" + +Suite* udp_suite(void); + +#endif diff --git a/external/badvpn_dns/misc/BRefTarget.h b/external/badvpn_dns/misc/BRefTarget.h new file mode 100644 index 00000000..4324605a --- /dev/null +++ b/external/badvpn_dns/misc/BRefTarget.h @@ -0,0 +1,114 @@ +/** + * @file BRefTarget.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_REF_TARGET_H +#define BADVPN_B_REF_TARGET_H + +#include + +#include +#include + +/** + * Represents a reference-counted object. + */ +typedef struct BRefTarget_s BRefTarget; + +/** + * Callback function called after the reference count of a {@link BRefTarget} + * reaches has reached zero. At this point the BRefTarget object has already + * been invalidated, i.e. {@link BRefTarget_Ref} must not be called on this + * object after this handler is called. + */ +typedef void (*BRefTarget_func_release) (BRefTarget *o); + +struct BRefTarget_s { + BRefTarget_func_release func_release; + int refcnt; + DebugObject d_obj; +}; + +/** + * Initializes a reference target object. The initial reference count of the object + * is 1. The \a func_release argument specifies the function to be called from + * {@link BRefTarget_Deref} when the reference count reaches zero. + */ +static void BRefTarget_Init (BRefTarget *o, BRefTarget_func_release func_release); + +/** + * Decrements the reference count of a reference target object. If the reference + * count has reached zero, the object's {@link BRefTarget_func_release} function + * is called, and the object is considered destroyed. + */ +static void BRefTarget_Deref (BRefTarget *o); + +/** + * Increments the reference count of a reference target object. + * Returns 1 on success and 0 on failure. + */ +static int BRefTarget_Ref (BRefTarget *o) WARN_UNUSED; + +static void BRefTarget_Init (BRefTarget *o, BRefTarget_func_release func_release) +{ + ASSERT(func_release) + + o->func_release = func_release; + o->refcnt = 1; + + DebugObject_Init(&o->d_obj); +} + +static void BRefTarget_Deref (BRefTarget *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->refcnt > 0) + + o->refcnt--; + + if (o->refcnt == 0) { + DebugObject_Free(&o->d_obj); + o->func_release(o); + } +} + +static int BRefTarget_Ref (BRefTarget *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->refcnt > 0) + + if (o->refcnt == INT_MAX) { + return 0; + } + + o->refcnt++; + + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/Utf16Decoder.h b/external/badvpn_dns/misc/Utf16Decoder.h new file mode 100644 index 00000000..819ac94c --- /dev/null +++ b/external/badvpn_dns/misc/Utf16Decoder.h @@ -0,0 +1,113 @@ +/** + * @file Utf16Decoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF16DECODER_H +#define BADVPN_UTF16DECODER_H + +#include + +#include + +/** + * Decodes UTF-16 data into Unicode characters. + */ +typedef struct { + int cont; + uint32_t ch; +} Utf16Decoder; + +/** + * Initializes the UTF-16 decoder. + * + * @param o the object + */ +static void Utf16Decoder_Init (Utf16Decoder *o); + +/** + * Inputs a 16-bit value to the decoder. + * + * @param o the object + * @param b 16-bit value to input + * @param out_ch will receive a Unicode character if this function returns 1. + * If written, the character will be in the range 0 - 0x10FFFF, + * excluding the surrogate range 0xD800 - 0xDFFF. + * @return 1 if a Unicode character has been written to *out_ch, 0 if not + */ +static int Utf16Decoder_Input (Utf16Decoder *o, uint16_t b, uint32_t *out_ch); + +void Utf16Decoder_Init (Utf16Decoder *o) +{ + o->cont = 0; +} + +int Utf16Decoder_Input (Utf16Decoder *o, uint16_t b, uint32_t *out_ch) +{ + // high surrogate + if (b >= UINT16_C(0xD800) && b <= UINT16_C(0xDBFF)) { + // set continuation state + o->cont = 1; + + // add high bits + o->ch = (uint32_t)(b - UINT16_C(0xD800)) << 10; + + return 0; + } + + // low surrogate + if (b >= UINT16_C(0xDC00) && b <= UINT16_C(0xDFFF)) { + // check continuation + if (!o->cont) { + return 0; + } + + // add low bits + o->ch |= (b - UINT16_C(0xDC00)); + + // reset state + o->cont = 0; + + // don't report surrogates + if (o->ch >= UINT32_C(0xD800) && o->ch <= UINT32_C(0xDFFF)) { + return 0; + } + + // return character + *out_ch = o->ch; + return 1; + } + + // reset state + o->cont = 0; + + // return character + *out_ch = b; + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/Utf16Encoder.h b/external/badvpn_dns/misc/Utf16Encoder.h new file mode 100644 index 00000000..4900b426 --- /dev/null +++ b/external/badvpn_dns/misc/Utf16Encoder.h @@ -0,0 +1,67 @@ +/** + * @file Utf16Encoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF16ENCODER_H +#define BADVPN_UTF16ENCODER_H + +#include + +/** + * Encodes a Unicode character into a sequence of 16-bit values according to UTF-16. + * + * @param ch Unicode character to encode + * @param out will receive the encoded 16-bit values. Must have space for 2 values. + * @return number of 16-bit values written, 0-2, with 0 meaning the character cannot + * be encoded + */ +static int Utf16Encoder_EncodeCharacter (uint32_t ch, uint16_t *out); + +int Utf16Encoder_EncodeCharacter (uint32_t ch, uint16_t *out) +{ + if (ch <= UINT32_C(0xFFFF)) { + // surrogates + if (ch >= UINT32_C(0xD800) && ch <= UINT32_C(0xDFFF)) { + return 0; + } + + out[0] = ch; + return 1; + } + + if (ch <= UINT32_C(0x10FFFF)) { + uint32_t x = ch - UINT32_C(0x10000); + out[0] = UINT32_C(0xD800) + (x >> 10); + out[1] = UINT32_C(0xDC00) + (x & UINT32_C(0x3FF)); + return 2; + } + + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/Utf8Decoder.h b/external/badvpn_dns/misc/Utf8Decoder.h new file mode 100644 index 00000000..c6412b18 --- /dev/null +++ b/external/badvpn_dns/misc/Utf8Decoder.h @@ -0,0 +1,143 @@ +/** + * @file Utf8Decoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF8DECODER_H +#define BADVPN_UTF8DECODER_H + +#include + +#include + +/** + * Decodes UTF-8 data into Unicode characters. + */ +typedef struct { + int bytes; + int pos; + uint32_t ch; +} Utf8Decoder; + +/** + * Initializes the UTF-8 decoder. + * + * @param o the object + */ +static void Utf8Decoder_Init (Utf8Decoder *o); + +/** + * Inputs a byte to the decoder. + * + * @param o the object + * @param b byte to input + * @param out_ch will receive a Unicode character if this function returns 1. + * If written, the character will be in the range 0 - 0x10FFFF, + * excluding the surrogate range 0xD800 - 0xDFFF. + * @return 1 if a Unicode character has been written to *out_ch, 0 if not + */ +static int Utf8Decoder_Input (Utf8Decoder *o, uint8_t b, uint32_t *out_ch); + +void Utf8Decoder_Init (Utf8Decoder *o) +{ + o->bytes = 0; +} + +int Utf8Decoder_Input (Utf8Decoder *o, uint8_t b, uint32_t *out_ch) +{ + // one-byte character + if ((b & 128) == 0) { + o->bytes = 0; + *out_ch = b; + return 1; + } + + // start of two-byte character + if ((b & 224) == 192) { + o->bytes = 2; + o->pos = 1; + o->ch = (uint32_t)(b & 31) << 6; + return 0; + } + + // start of three-byte character + if ((b & 240) == 224) { + o->bytes = 3; + o->pos = 1; + o->ch = (uint32_t)(b & 15) << 12; + return 0; + } + + // start of four-byte character + if ((b & 248) == 240) { + o->bytes = 4; + o->pos = 1; + o->ch = (uint32_t)(b & 7) << 18; + return 0; + } + + // continuation of multi-byte character + if ((b & 192) == 128 && o->bytes > 0) { + ASSERT(o->bytes <= 4) + ASSERT(o->pos > 0) + ASSERT(o->pos < o->bytes) + + // add bits from this byte + o->ch |= (uint32_t)(b & 63) << (6 * (o->bytes - o->pos - 1)); + + // end of multi-byte character? + if (o->pos == o->bytes - 1) { + // reset state + o->bytes = 0; + + // don't report out-of-range characters + if (o->ch > UINT32_C(0x10FFFF)) { + return 0; + } + + // don't report surrogates + if (o->ch >= UINT32_C(0xD800) && o->ch <= UINT32_C(0xDFFF)) { + return 0; + } + + *out_ch = o->ch; + return 1; + } + + // increment byte index + o->pos++; + + return 0; + } + + // error, reset state + o->bytes = 0; + + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/Utf8Encoder.h b/external/badvpn_dns/misc/Utf8Encoder.h new file mode 100644 index 00000000..00eb5e68 --- /dev/null +++ b/external/badvpn_dns/misc/Utf8Encoder.h @@ -0,0 +1,81 @@ +/** + * @file Utf8Encoder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UTF8ENCODER_H +#define BADVPN_UTF8ENCODER_H + +#include + +/** + * Encodes a Unicode character into a sequence of bytes according to UTF-8. + * + * @param ch Unicode character to encode + * @param out will receive the encoded bytes. Must have space for 4 bytes. + * @return number of bytes written, 0-4, with 0 meaning the character cannot + * be encoded + */ +static int Utf8Encoder_EncodeCharacter (uint32_t ch, uint8_t *out); + +int Utf8Encoder_EncodeCharacter (uint32_t ch, uint8_t *out) +{ + if (ch <= UINT32_C(0x007F)) { + out[0] = ch; + return 1; + } + + if (ch <= UINT32_C(0x07FF)) { + out[0] = (0xC0 | (ch >> 6)); + out[1] = (0x80 | ((ch >> 0) & 0x3F)); + return 2; + } + + if (ch <= UINT32_C(0xFFFF)) { + // surrogates + if (ch >= UINT32_C(0xD800) && ch <= UINT32_C(0xDFFF)) { + return 0; + } + + out[0] = (0xE0 | (ch >> 12)); + out[1] = (0x80 | ((ch >> 6) & 0x3F)); + out[2] = (0x80 | ((ch >> 0) & 0x3F)); + return 3; + } + + if (ch < UINT32_C(0x10FFFF)) { + out[0] = (0xF0 | (ch >> 18)); + out[1] = (0x80 | ((ch >> 12) & 0x3F)); + out[2] = (0x80 | ((ch >> 6) & 0x3F)); + out[3] = (0x80 | ((ch >> 0) & 0x3F)); + return 4; + } + + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/arp_proto.h b/external/badvpn_dns/misc/arp_proto.h new file mode 100644 index 00000000..71a6c98d --- /dev/null +++ b/external/badvpn_dns/misc/arp_proto.h @@ -0,0 +1,60 @@ +/** + * @file arp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the ARP protocol. + */ + +#ifndef BADVPN_ARP_PROTO_H +#define BADVPN_ARP_PROTO_H + +#include + +#include + +#define ARP_HARDWARE_TYPE_ETHERNET 1 + +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 + +B_START_PACKED +struct arp_packet { + uint16_t hardware_type; + uint16_t protocol_type; + uint8_t hardware_size; + uint8_t protocol_size; + uint16_t opcode; + uint8_t sender_mac[6]; + uint32_t sender_ip; + uint8_t target_mac[6]; + uint32_t target_ip; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/misc/array_length.h b/external/badvpn_dns/misc/array_length.h new file mode 100644 index 00000000..15de964c --- /dev/null +++ b/external/badvpn_dns/misc/array_length.h @@ -0,0 +1,35 @@ +/** + * @file array_length.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_ARRAY_LENGTH +#define BADVPN_ARRAY_LENGTH + +#define B_ARRAY_LENGTH(arr) (sizeof((arr)) / sizeof((arr)[0])) + +#endif diff --git a/external/badvpn_dns/misc/balign.h b/external/badvpn_dns/misc/balign.h new file mode 100644 index 00000000..57152afb --- /dev/null +++ b/external/badvpn_dns/misc/balign.h @@ -0,0 +1,76 @@ +/** + * @file balign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Integer alignment macros. + */ + +#ifndef BADVPN_MISC_BALIGN_H +#define BADVPN_MISC_BALIGN_H + +#include +#include + +/** + * Checks if aligning x up to n would overflow. + */ +static int balign_up_overflows (size_t x, size_t n) +{ + size_t r = x % n; + + return (r && x > SIZE_MAX - (n - r)); +} + +/** + * Aligns x up to n. + */ +static size_t balign_up (size_t x, size_t n) +{ + size_t r = x % n; + return (r ? x + (n - r) : x); +} + +/** + * Aligns x down to n. + */ +static size_t balign_down (size_t x, size_t n) +{ + return (x - (x % n)); +} + +/** + * Calculates the quotient of a and b, rounded up. + */ +static size_t bdivide_up (size_t a, size_t b) +{ + size_t r = a % b; + return (r > 0 ? a / b + 1 : a / b); +} + +#endif diff --git a/external/badvpn_dns/misc/balloc.h b/external/badvpn_dns/misc/balloc.h new file mode 100644 index 00000000..7d2d54fc --- /dev/null +++ b/external/badvpn_dns/misc/balloc.h @@ -0,0 +1,248 @@ +/** + * @file balloc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Memory allocation functions. + */ + +#ifndef BADVPN_MISC_BALLOC_H +#define BADVPN_MISC_BALLOC_H + +#include +#include +#include +#include + +#include +#include +#include + +/** + * Allocates memory. + * + * @param bytes number of bytes to allocate. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAlloc (size_t bytes); + +/** + * Frees memory. + * + * @param m memory to free. Must have been obtained with {@link BAlloc}, + * {@link BAllocArray}, or {@link BAllocArray2}. May be NULL; + * in this case, this function does nothing. + */ +static void BFree (void *m); + +/** + * Changes the size of a memory block. On success, the memory block + * may be moved to a different address. + * + * @param m pointer to a memory block obtained from {@link BAlloc} + * or other functions in this group. If this is NULL, the + * call is equivalent to {@link BAlloc}(bytes). + * @param bytes new size of the memory block + * @return new pointer to the memory block, or NULL on failure + */ +static void * BRealloc (void *m, size_t bytes); + +/** + * Allocates memory, with size given as a {@link bsize_t}. + * + * @param bytes number of bytes to allocate. If the size is overflow, + * this function will return NULL. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocSize (bsize_t bytes); + +/** + * Allocates memory for an array. + * A check is first done to make sure the multiplication doesn't overflow; + * otherwise, this is equivalent to {@link BAlloc}(count * bytes). + * This may be slightly faster if 'bytes' is constant, because a division + * with 'bytes' is performed. + * + * @param count number of elements. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray (size_t count, size_t bytes); + +/** + * Reallocates memory that was allocated using one of the allocation + * functions in this file. On success, the memory may be moved to a + * different address, leaving the old address invalid. + * + * @param mem pointer to an existing memory block. May be NULL, in which + * case this is equivalent to {@link BAllocArray}. + * @param count number of elements for reallocation + * @param bytes size of one array element for reallocation + * @return a non-NULL pointer to the address of the reallocated memory + * block, or NULL on failure. On failure, the original memory + * block is left intact. + */ +static void * BReallocArray (void *mem, size_t count, size_t bytes); + +/** + * Allocates memory for a two-dimensional array. + * + * Checks are first done to make sure the multiplications don't overflow; + * otherwise, this is equivalent to {@link BAlloc}((count2 * (count1 * bytes)). + * + * @param count2 number of elements in one dimension. + * @param count1 number of elements in the other dimension. + * @param bytes size of one array element. + * @return a non-NULL pointer to the memory, or NULL on failure. + * The memory allocated can be freed using {@link BFree}. + */ +static void * BAllocArray2 (size_t count2, size_t count1, size_t bytes); + +/** + * Adds to a size_t with overflow detection. + * + * @param s pointer to a size_t to add to + * @param add number to add + * @return 1 on success, 0 on failure + */ +static int BSizeAdd (size_t *s, size_t add); + +/** + * Aligns a size_t upwards with overflow detection. + * + * @param s pointer to a size_t to align + * @param align alignment value. Must be >0. + * @return 1 on success, 0 on failure + */ +static int BSizeAlign (size_t *s, size_t align); + +void * BAlloc (size_t bytes) +{ + if (bytes == 0) { + return malloc(1); + } + + return malloc(bytes); +} + +void BFree (void *m) +{ + free(m); +} + +void * BRealloc (void *m, size_t bytes) +{ + if (bytes == 0) { + return realloc(m, 1); + } + + return realloc(m, bytes); +} + +void * BAllocSize (bsize_t bytes) +{ + if (bytes.is_overflow) { + return NULL; + } + + return BAlloc(bytes.value); +} + +void * BAllocArray (size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return malloc(1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return BAlloc(count * bytes); +} + +void * BReallocArray (void *mem, size_t count, size_t bytes) +{ + if (count == 0 || bytes == 0) { + return realloc(mem, 1); + } + + if (count > SIZE_MAX / bytes) { + return NULL; + } + + return realloc(mem, count * bytes); +} + +void * BAllocArray2 (size_t count2, size_t count1, size_t bytes) +{ + if (count2 == 0 || count1 == 0 || bytes == 0) { + return malloc(1); + } + + if (count1 > SIZE_MAX / bytes) { + return NULL; + } + + if (count2 > SIZE_MAX / (count1 * bytes)) { + return NULL; + } + + return BAlloc(count2 * (count1 * bytes)); +} + +int BSizeAdd (size_t *s, size_t add) +{ + ASSERT(s) + + if (add > SIZE_MAX - *s) { + return 0; + } + *s += add; + return 1; +} + +int BSizeAlign (size_t *s, size_t align) +{ + ASSERT(s) + ASSERT(align > 0) + + size_t mod = *s % align; + if (mod > 0) { + if (align - mod > SIZE_MAX - *s) { + return 0; + } + *s += align - mod; + } + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/blimits.h b/external/badvpn_dns/misc/blimits.h new file mode 100644 index 00000000..8bcdc2a7 --- /dev/null +++ b/external/badvpn_dns/misc/blimits.h @@ -0,0 +1,60 @@ +/** + * @file blimits.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BLIMITS_H +#define BADVPN_BLIMITS_H + +#include + +#define BTYPE_IS_SIGNED(type) ((type)-1 < 0) + +#define BSIGNED_TYPE_MIN(type) ( \ + sizeof(type) == 1 ? INT8_MIN : ( \ + sizeof(type) == 2 ? INT16_MIN : ( \ + sizeof(type) == 4 ? INT32_MIN : ( \ + sizeof(type) == 8 ? INT64_MIN : 0)))) + +#define BSIGNED_TYPE_MAX(type) ( \ + sizeof(type) == 1 ? INT8_MAX : ( \ + sizeof(type) == 2 ? INT16_MAX : ( \ + sizeof(type) == 4 ? INT32_MAX : ( \ + sizeof(type) == 8 ? INT64_MAX : 0)))) + +#define BUNSIGNED_TYPE_MIN(type) ((type)0) + +#define BUNSIGNED_TYPE_MAX(type) ( \ + sizeof(type) == 1 ? UINT8_MAX : ( \ + sizeof(type) == 2 ? UINT16_MAX : ( \ + sizeof(type) == 4 ? UINT32_MAX : ( \ + sizeof(type) == 8 ? UINT64_MAX : 0)))) + +#define BTYPE_MIN(type) (BTYPE_IS_SIGNED(type) ? BSIGNED_TYPE_MIN(type) : BUNSIGNED_TYPE_MIN(type)) +#define BTYPE_MAX(type) (BTYPE_IS_SIGNED(type) ? BSIGNED_TYPE_MAX(type) : BUNSIGNED_TYPE_MAX(type)) + +#endif diff --git a/external/badvpn_dns/misc/bsize.h b/external/badvpn_dns/misc/bsize.h new file mode 100644 index 00000000..2d724df2 --- /dev/null +++ b/external/badvpn_dns/misc/bsize.h @@ -0,0 +1,117 @@ +/** + * @file bsize.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Arithmetic with overflow detection. + */ + +#ifndef BADVPN_MISC_BSIZE_H +#define BADVPN_MISC_BSIZE_H + +#include +#include +#include + +typedef struct { + int is_overflow; + size_t value; +} bsize_t; + +static bsize_t bsize_fromsize (size_t v); +static bsize_t bsize_fromint (int v); +static bsize_t bsize_overflow (void); +static int bsize_tosize (bsize_t s, size_t *out); +static int bsize_toint (bsize_t s, int *out); +static bsize_t bsize_add (bsize_t s1, bsize_t s2); +static bsize_t bsize_max (bsize_t s1, bsize_t s2); +static bsize_t bsize_mul (bsize_t s1, bsize_t s2); + +bsize_t bsize_fromsize (size_t v) +{ + bsize_t s = {0, v}; + return s; +} + +bsize_t bsize_fromint (int v) +{ + bsize_t s = {(v < 0 || v > SIZE_MAX), v}; + return s; +} + +static bsize_t bsize_overflow (void) +{ + bsize_t s = {1, 0}; + return s; +} + +int bsize_tosize (bsize_t s, size_t *out) +{ + if (s.is_overflow) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +int bsize_toint (bsize_t s, int *out) +{ + if (s.is_overflow || s.value > INT_MAX) { + return 0; + } + + if (out) { + *out = s.value; + } + + return 1; +} + +bsize_t bsize_add (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || s2.value > SIZE_MAX - s1.value), (s1.value + s2.value)}; + return s; +} + +bsize_t bsize_max (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow), ((s1.value > s2.value) * s1.value + (s1.value <= s2.value) * s2.value)}; + return s; +} + +bsize_t bsize_mul (bsize_t s1, bsize_t s2) +{ + bsize_t s = {(s1.is_overflow || s2.is_overflow || (s1.value != 0 && s2.value > SIZE_MAX / s1.value)), (s1.value * s2.value)}; + return s; +} + +#endif diff --git a/external/badvpn_dns/misc/bsort.h b/external/badvpn_dns/misc/bsort.h new file mode 100644 index 00000000..24d7a66a --- /dev/null +++ b/external/badvpn_dns/misc/bsort.h @@ -0,0 +1,69 @@ +/** + * @file bsort.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Sorting functions. + */ + +#ifndef BADVPN_MISC_BSORT_H +#define BADVPN_MISC_BSORT_H + +#include +#include +#include + +#include +#include + +typedef int (*BSort_comparator) (const void *e1, const void *e2); + +static void BInsertionSort (void *arr, size_t count, size_t esize, BSort_comparator compatator, void *temp); + +void BInsertionSort (void *arr, size_t count, size_t esize, BSort_comparator compatator, void *temp) +{ + ASSERT(esize > 0) + + for (size_t i = 0; i < count; i++) { + size_t j = i; + while (j > 0) { + uint8_t *x = (uint8_t *)arr + (j - 1) * esize; + uint8_t *y = (uint8_t *)arr + j * esize; + int c = compatator(x, y); + if (c <= 0) { + break; + } + memcpy(temp, x, esize); + memcpy(x, y, esize); + memcpy(y, temp, esize); + j--; + } + } +} + +#endif diff --git a/external/badvpn_dns/misc/bstring.h b/external/badvpn_dns/misc/bstring.h new file mode 100644 index 00000000..caef1062 --- /dev/null +++ b/external/badvpn_dns/misc/bstring.h @@ -0,0 +1,140 @@ +/** + * @file bstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BSTRING_H +#define BADVPN_BSTRING_H + +#include + +#include +#include + +#define BSTRING_TYPE_STATIC 5 +#define BSTRING_TYPE_DYNAMIC 7 +#define BSTRING_TYPE_EXTERNAL 11 + +#define BSTRING_STATIC_SIZE 23 +#define BSTRING_STATIC_MAX (BSTRING_STATIC_SIZE - 1) + +typedef struct { + union { + struct { + char type; + char static_string[BSTRING_STATIC_SIZE]; + } us; + struct { + char type; + char *dynamic_string; + } ud; + struct { + char type; + const char *external_string; + } ue; + } u; +} BString; + +static void BString__assert (BString *o) +{ + switch (o->u.us.type) { + case BSTRING_TYPE_STATIC: + case BSTRING_TYPE_DYNAMIC: + case BSTRING_TYPE_EXTERNAL: + return; + } + + ASSERT(0); +} + +static int BString_Init (BString *o, const char *str) +{ + if (strlen(str) <= BSTRING_STATIC_MAX) { + strcpy(o->u.us.static_string, str); + o->u.us.type = BSTRING_TYPE_STATIC; + } else { + if (!(o->u.ud.dynamic_string = malloc(strlen(str) + 1))) { + return 0; + } + strcpy(o->u.ud.dynamic_string, str); + o->u.ud.type = BSTRING_TYPE_DYNAMIC; + } + + BString__assert(o); + return 1; +} + +static void BString_InitStatic (BString *o, const char *str) +{ + ASSERT(strlen(str) <= BSTRING_STATIC_MAX) + + strcpy(o->u.us.static_string, str); + o->u.us.type = BSTRING_TYPE_STATIC; + + BString__assert(o); +} + +static void BString_InitExternal (BString *o, const char *str) +{ + o->u.ue.external_string = str; + o->u.ue.type = BSTRING_TYPE_EXTERNAL; + + BString__assert(o); +} + +static void BString_InitAllocated (BString *o, char *str) +{ + o->u.ud.dynamic_string = str; + o->u.ud.type = BSTRING_TYPE_DYNAMIC; + + BString__assert(o); +} + +static void BString_Free (BString *o) +{ + BString__assert(o); + + if (o->u.ud.type == BSTRING_TYPE_DYNAMIC) { + free(o->u.ud.dynamic_string); + } +} + +static const char * BString_Get (BString *o) +{ + BString__assert(o); + + switch (o->u.us.type) { + case BSTRING_TYPE_STATIC: return o->u.us.static_string; + case BSTRING_TYPE_DYNAMIC: return o->u.ud.dynamic_string; + case BSTRING_TYPE_EXTERNAL: return o->u.ue.external_string; + } + + ASSERT(0); + return NULL; +} + +#endif diff --git a/external/badvpn_dns/misc/byteorder.h b/external/badvpn_dns/misc/byteorder.h new file mode 100644 index 00000000..055b0a59 --- /dev/null +++ b/external/badvpn_dns/misc/byteorder.h @@ -0,0 +1,196 @@ +/** + * @file byteorder.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Byte order conversion functions. + * + * hton* functions convert from host to big-endian (network) byte order. + * htol* functions convert from host to little-endian byte order. + * ntoh* functions convert from big-endian (network) to host byte order. + * ltoh* functions convert from little-endian to host byte order. + */ + +#ifndef BADVPN_MISC_BYTEORDER_H +#define BADVPN_MISC_BYTEORDER_H + +#if (defined(BADVPN_LITTLE_ENDIAN) + defined(BADVPN_BIG_ENDIAN)) != 1 +#error Unknown byte order or too many byte orders +#endif + +#include + +static uint16_t badvpn_reverse16 (uint16_t x) +{ + uint16_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+1); + *((uint8_t *)&y+1) = *((uint8_t *)&x+0); + return y; +} + +static uint32_t badvpn_reverse32 (uint32_t x) +{ + uint32_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+3); + *((uint8_t *)&y+1) = *((uint8_t *)&x+2); + *((uint8_t *)&y+2) = *((uint8_t *)&x+1); + *((uint8_t *)&y+3) = *((uint8_t *)&x+0); + return y; +} + +static uint64_t badvpn_reverse64 (uint64_t x) +{ + uint64_t y; + *((uint8_t *)&y+0) = *((uint8_t *)&x+7); + *((uint8_t *)&y+1) = *((uint8_t *)&x+6); + *((uint8_t *)&y+2) = *((uint8_t *)&x+5); + *((uint8_t *)&y+3) = *((uint8_t *)&x+4); + *((uint8_t *)&y+4) = *((uint8_t *)&x+3); + *((uint8_t *)&y+5) = *((uint8_t *)&x+2); + *((uint8_t *)&y+6) = *((uint8_t *)&x+1); + *((uint8_t *)&y+7) = *((uint8_t *)&x+0); + return y; +} + +static uint8_t hton8 (uint8_t x) +{ + return x; +} + +static uint8_t htol8 (uint8_t x) +{ + return x; +} + +#if defined(BADVPN_LITTLE_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t hton32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t hton64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +static uint16_t htol16 (uint16_t x) +{ + return x; +} + +static uint32_t htol32 (uint32_t x) +{ + return x; +} + +static uint64_t htol64 (uint64_t x) +{ + return x; +} + +#elif defined(BADVPN_BIG_ENDIAN) + +static uint16_t hton16 (uint16_t x) +{ + return x; +} + +static uint32_t hton32 (uint32_t x) +{ + return x; +} + +static uint64_t hton64 (uint64_t x) +{ + return x; +} + +static uint16_t htol16 (uint16_t x) +{ + return badvpn_reverse16(x); +} + +static uint32_t htol32 (uint32_t x) +{ + return badvpn_reverse32(x); +} + +static uint64_t htol64 (uint64_t x) +{ + return badvpn_reverse64(x); +} + +#endif + +static uint8_t ntoh8 (uint8_t x) +{ + return hton8(x); +} + +static uint16_t ntoh16 (uint16_t x) +{ + return hton16(x); +} + +static uint32_t ntoh32 (uint32_t x) +{ + return hton32(x); +} + +static uint64_t ntoh64 (uint64_t x) +{ + return hton64(x); +} + +static uint8_t ltoh8 (uint8_t x) +{ + return htol8(x); +} + +static uint16_t ltoh16 (uint16_t x) +{ + return htol16(x); +} + +static uint32_t ltoh32 (uint32_t x) +{ + return htol32(x); +} + +static uint64_t ltoh64 (uint64_t x) +{ + return htol64(x); +} + +#endif diff --git a/external/badvpn_dns/misc/cmdline.h b/external/badvpn_dns/misc/cmdline.h new file mode 100644 index 00000000..fba899ac --- /dev/null +++ b/external/badvpn_dns/misc/cmdline.h @@ -0,0 +1,181 @@ +/** + * @file cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Command line construction functions. + */ + +#ifndef BADVPN_MISC_CMDLINE_H +#define BADVPN_MISC_CMDLINE_H + +#include +#include + +#include +#include +#include +#include + +typedef struct { + struct ExpArray arr; + size_t n; +} CmdLine; + +static int CmdLine_Init (CmdLine *c); +static void CmdLine_Free (CmdLine *c); +static int CmdLine_Append (CmdLine *c, const char *str); +static int CmdLine_AppendNoNull (CmdLine *c, const char *str, size_t str_len); +static int CmdLine_AppendCstring (CmdLine *c, b_cstring cstr, size_t offset, size_t length); +static int CmdLine_AppendMulti (CmdLine *c, int num, ...); +static int CmdLine_Finish (CmdLine *c); +static char ** CmdLine_Get (CmdLine *c); + +static int _CmdLine_finished (CmdLine *c) +{ + return (c->n > 0 && ((char **)c->arr.v)[c->n - 1] == NULL); +} + +int CmdLine_Init (CmdLine *c) +{ + if (!ExpArray_init(&c->arr, sizeof(char *), 16)) { + return 0; + } + + c->n = 0; + + return 1; +} + +void CmdLine_Free (CmdLine *c) +{ + for (size_t i = 0; i < c->n; i++) { + free(((char **)c->arr.v)[i]); + } + + free(c->arr.v); +} + +int CmdLine_Append (CmdLine *c, const char *str) +{ + ASSERT(str) + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = strdup(str))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendNoNull (CmdLine *c, const char *str, size_t str_len) +{ + ASSERT(str) + ASSERT(!memchr(str, '\0', str_len)) + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = b_strdup_bin(str, str_len))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendCstring (CmdLine *c, b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + ASSERT(!b_cstring_memchr(cstr, offset, length, '\0', NULL)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + if (!(((char **)c->arr.v)[c->n] = b_cstring_strdup(cstr, offset, length))) { + return 0; + } + + c->n++; + + return 1; +} + +int CmdLine_AppendMulti (CmdLine *c, int num, ...) +{ + int res = 1; + + va_list vl; + va_start(vl, num); + + for (int i = 0; i < num; i++) { + const char *str = va_arg(vl, const char *); + if (!CmdLine_Append(c, str)) { + res = 0; + break; + } + } + + va_end(vl); + + return res; +} + +int CmdLine_Finish (CmdLine *c) +{ + ASSERT(!_CmdLine_finished(c)) + + if (!ExpArray_resize(&c->arr, c->n + 1)) { + return 0; + } + + ((char **)c->arr.v)[c->n] = NULL; + + c->n++; + + return 1; +} + +char ** CmdLine_Get (CmdLine *c) +{ + ASSERT(_CmdLine_finished(c)) + + return (char **)c->arr.v; +} + +#endif diff --git a/external/badvpn_dns/misc/compare.h b/external/badvpn_dns/misc/compare.h new file mode 100644 index 00000000..8d1a1b99 --- /dev/null +++ b/external/badvpn_dns/misc/compare.h @@ -0,0 +1,37 @@ +/** + * @file compare.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPARE_H +#define BADVPN_COMPARE_H + +#define B_COMPARE(a, b) (((a) > (b)) - ((a) < (b))) +#define B_COMPARE_COMBINE(cmp1, cmp2) ((cmp1) ? (cmp1) : (cmp2)) +#define B_COMPARE2(a, b, c, d) B_COMPARE_COMBINE(B_COMPARE((a), (b)), B_COMPARE((c), (d))) + +#endif diff --git a/external/badvpn_dns/misc/concat_strings.h b/external/badvpn_dns/misc/concat_strings.h new file mode 100644 index 00000000..30330bc9 --- /dev/null +++ b/external/badvpn_dns/misc/concat_strings.h @@ -0,0 +1,85 @@ +/** + * @file concat_strings.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for concatenating strings. + */ + +#ifndef BADVPN_MISC_CONCAT_STRINGS_H +#define BADVPN_MISC_CONCAT_STRINGS_H + +#include +#include +#include + +#include + +static char * concat_strings (int num, ...) +{ + ASSERT(num >= 0) + + // calculate sum of lengths + size_t sum = 0; + va_list ap; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + if (str_len > SIZE_MAX - 1 - sum) { + va_end(ap); + return NULL; + } + sum += str_len; + } + va_end(ap); + + // allocate memory + char *res_str = (char *)malloc(sum + 1); + if (!res_str) { + return NULL; + } + + // copy strings + sum = 0; + va_start(ap, num); + for (int i = 0; i < num; i++) { + const char *str = va_arg(ap, const char *); + size_t str_len = strlen(str); + memcpy(res_str + sum, str, str_len); + sum += str_len; + } + va_end(ap); + + // terminate + res_str[sum] = '\0'; + + return res_str; +} + +#endif diff --git a/external/badvpn_dns/misc/cstring.h b/external/badvpn_dns/misc/cstring.h new file mode 100644 index 00000000..bd8de750 --- /dev/null +++ b/external/badvpn_dns/misc/cstring.h @@ -0,0 +1,347 @@ +/** + * @file cstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_COMPOSED_STRING_H +#define BADVPN_COMPOSED_STRING_H + +#include +#include +#include + +#include +#include + +struct b_cstring_s; + +/** + * Callback function which is called by {@link b_cstring_get} to access the underlying resource. + * \a cstr points to the cstring being accessed, and the callback can use the userN members to + * retrieve any state information. + * \a offset is the offset from the beginning of the string; offset < cstr->length. + * This callback must set *\a out_length and return a pointer, representing a continuous + * region of the string that starts at the byte at index \a offset. Returning a region that + * spans past the end of the string is allowed. + */ +typedef const char * (*b_cstring_func) (const struct b_cstring_s *cstr, size_t offset, size_t *out_length); + +/** + * An abstract string which is not necessarily continuous. Given a cstring, its length + * can be determined by reading the 'length' member, and its data can be read using + * {@link b_cstring_get} (which internally invokes the {@link b_cstring_func} callback). + */ +typedef struct b_cstring_s { + size_t length; + b_cstring_func func; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user1; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user2; + union { + size_t size; + void *ptr; + void (*fptr) (void); + } user3; +} b_cstring; + +/** + * Makes a cstring pointing to a buffer. + * \a data may be NULL if \a length is 0. + */ +static b_cstring b_cstring_make_buf (const char *data, size_t length); + +/** + * Makes a cstring which represents an empty string. + */ +static b_cstring b_cstring_make_empty (void); + +/** + * Retrieves a pointer to a continuous region of the string. + * \a offset specifies the starting offset of the region to retrieve, and must be < cstr.length. + * \a maxlen specifies the maximum length of the returned region, and must be > 0. + * The length of the region will be stored in *\a out_chunk_len, and it will always be > 0. + * It is possible that the returned region spans past the end of the string, unless limited + * by \a maxlen. The pointer to the region will be returned; it will point to the byte + * at offset exactly \a offset into the string. + */ +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len); + +/** + * Retrieves the byte in the string at position \a pos. + */ +static char b_cstring_at (b_cstring cstr, size_t pos); + +/** + * Asserts that the range given by \a offset and \a length is valid for the string. + */ +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length); + +/** + * Copies a range to an external buffer. + */ +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest); + +/** + * Performs a memcmp-like operation on the given ranges of two cstrings. + */ +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length); + +/** + * Determines if a range within a string is equal to the bytes in an external buffer. + */ +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data); + +/** + * Determines if a range within a string contains the byte \a ch. + * Returns 1 if it does, and 0 if it does not. If it does contain it, and \a out_pos is not + * NULL, *\a out_pos is set to the index of the first matching byte in the range, relative + * to the beginning of the range \a offset. + */ +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos); + +/** + * Allocates a buffer for a range and copies it. The buffer is allocated using {@link BAlloc}. + * An extra null byte will be appended. On failure, returns NULL. + */ +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length); + +/** + * Macro which iterates the continuous regions of a range within a cstring. + * For reach region, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a rel_pos_var, \a chunk_data_var and \a chunk_length_var specify names of variables + * which will be available in \a body. + * \a rel_pos_var will hold the offset (size_t) of the current continuous region, relative + * to \a offset. + * \a chunk_data_var will hold a pointer (const char *) to the beginning of the region, and + * \a chunk_length_var will hold its length (size_t). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + * + * See the implementation of {@link b_cstring_copy_to_buf} for a usage example. + */ +#define B_CSTRING_LOOP_RANGE(cstr, offset, length, rel_pos_var, chunk_data_var, chunk_length_var, body) \ +{ \ + size_t rel_pos_var = 0; \ + while (rel_pos_var < (length)) { \ + size_t chunk_length_var; \ + const char *chunk_data_var = b_cstring_get((cstr), (offset) + rel_pos_var, (length) - rel_pos_var, &chunk_length_var); \ + { body } \ + rel_pos_var += chunk_length_var; \ + } \ +} + +/** + * Like {@link B_CSTRING_LOOP_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP(cstr, rel_pos_var, chunk_data_var, chunk_length_var, body) B_CSTRING_LOOP_RANGE(cstr, 0, (cstr).length, rel_pos_var, chunk_data_var, chunk_length_var, body) + +/** + * Macro which iterates the characters of a range within a cstring. + * For each character, the statements in \a body are executed, in order. + * \a cstr is the string to be iterated. + * \a offset and \a length specify the range of the string to iterate; they must + * refer to a valid range for the string. + * \a char_rel_pos_var and \a char_var specify names of variables which will be + * available in \a body. + * \a char_rel_pos_var will hold the position (size_t) of the current character + * relative to \a offset. + * \a char_var will hold the current character (char). + * + * Note: \a cstr, \a offset and \a length may be evaluated multiple times, or not at all. + * Note: do not use 'continue' or 'break' from inside the body, their behavior depends + * on the internal implementation of this macro. + */ +#define B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, char_rel_pos_var, char_var, body) \ +B_CSTRING_LOOP_RANGE(cstr, offset, length, b_cstring_loop_chars_pos, b_cstring_loop_chars_chunk_data, b_cstring_loop_chars_chunk_length, { \ + for (size_t b_cstring_loop_chars_chunk_pos = 0; b_cstring_loop_chars_chunk_pos < b_cstring_loop_chars_chunk_length; b_cstring_loop_chars_chunk_pos++) { \ + char char_rel_pos_var = b_cstring_loop_chars_pos + b_cstring_loop_chars_chunk_pos; \ + B_USE(char_rel_pos_var) \ + char char_var = b_cstring_loop_chars_chunk_data[b_cstring_loop_chars_chunk_pos]; \ + { body } \ + } \ +}) + +/** + * Like {@link B_CSTRING_LOOP_CHARS_RANGE}, but iterates the entire string, + * i.e. offset==0 and length==cstr.length. + */ +#define B_CSTRING_LOOP_CHARS(cstr, char_rel_pos_var, char_var, body) B_CSTRING_LOOP_CHARS_RANGE(cstr, 0, (cstr).length, char_rel_pos_var, char_var, body) + +static const char * b_cstring__buf_func (const b_cstring *cstr, size_t offset, size_t *out_length) +{ + ASSERT(offset < cstr->length) + ASSERT(out_length) + ASSERT(cstr->func == b_cstring__buf_func) + ASSERT(cstr->user1.ptr) + + *out_length = cstr->length - offset; + return (const char *)cstr->user1.ptr + offset; +} + +static b_cstring b_cstring_make_buf (const char *data, size_t length) +{ + ASSERT(length == 0 || data) + + b_cstring cstr; + cstr.length = length; + cstr.func = b_cstring__buf_func; + cstr.user1.ptr = (void *)data; + return cstr; +} + +static b_cstring b_cstring_make_empty (void) +{ + b_cstring cstr; + cstr.length = 0; + cstr.func = NULL; + return cstr; +} + +static const char * b_cstring_get (b_cstring cstr, size_t offset, size_t maxlen, size_t *out_chunk_len) +{ + ASSERT(offset < cstr.length) + ASSERT(maxlen > 0) + ASSERT(out_chunk_len) + ASSERT(cstr.func) + + const char *data = cstr.func(&cstr, offset, out_chunk_len); + ASSERT(data) + ASSERT(*out_chunk_len > 0) + + if (*out_chunk_len > maxlen) { + *out_chunk_len = maxlen; + } + + return data; +} + +static char b_cstring_at (b_cstring cstr, size_t pos) +{ + ASSERT(pos < cstr.length) + ASSERT(cstr.func) + + size_t chunk_len; + const char *data = cstr.func(&cstr, pos, &chunk_len); + ASSERT(data) + ASSERT(chunk_len > 0) + + return *data; +} + +static void b_cstring_assert_range (b_cstring cstr, size_t offset, size_t length) +{ + ASSERT(offset <= cstr.length) + ASSERT(length <= cstr.length - offset) +} + +static void b_cstring_copy_to_buf (b_cstring cstr, size_t offset, size_t length, char *dest) +{ + b_cstring_assert_range(cstr, offset, length); + ASSERT(length == 0 || dest) + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + memcpy(dest + pos, chunk_data, chunk_length); + }) +} + +static int b_cstring_memcmp (b_cstring cstr1, b_cstring cstr2, size_t offset1, size_t offset2, size_t length) +{ + b_cstring_assert_range(cstr1, offset1, length); + b_cstring_assert_range(cstr2, offset2, length); + + B_CSTRING_LOOP_RANGE(cstr1, offset1, length, pos1, chunk_data1, chunk_len1, { + B_CSTRING_LOOP_RANGE(cstr2, offset2 + pos1, chunk_len1, pos2, chunk_data2, chunk_len2, { + int cmp = memcmp(chunk_data1 + pos2, chunk_data2, chunk_len2); + if (cmp) { + return cmp; + } + }) + }) + + return 0; +} + +static int b_cstring_equals_buffer (b_cstring cstr, size_t offset, size_t length, const char *data) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_len, { + if (memcmp(chunk_data, data + pos, chunk_len)) { + return 0; + } + }) + + return 1; +} + +static int b_cstring_memchr (b_cstring cstr, size_t offset, size_t length, char ch, size_t *out_pos) +{ + b_cstring_assert_range(cstr, offset, length); + + B_CSTRING_LOOP_CHARS_RANGE(cstr, offset, length, cur_ch_pos, cur_ch, { + if (cur_ch == ch) { + if (out_pos) { + *out_pos = cur_ch_pos; + } + return 1; + } + }) + + return 0; +} + +static char * b_cstring_strdup (b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == SIZE_MAX) { + return NULL; + } + + char *buf = (char *)BAlloc(length + 1); + if (buf) { + b_cstring_copy_to_buf(cstr, offset, length, buf); + buf[length] = '\0'; + } + + return buf; +} + +#endif diff --git a/external/badvpn_dns/misc/dead.h b/external/badvpn_dns/misc/dead.h new file mode 100644 index 00000000..7f574219 --- /dev/null +++ b/external/badvpn_dns/misc/dead.h @@ -0,0 +1,134 @@ +/** + * @file dead.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dead mechanism definitions. + * + * The dead mechanism is a way for a piece of code to detect whether some + * specific event has occured during some operation (usually during calling + * a user-provided handler function), without requiring access to memory + * that might no longer be available because of the event. + * + * It works somehow like that: + * + * First a dead variable ({@link dead_t}) is allocated somewhere, and + * initialized with {@link DEAD_INIT}, e.g.: + * DEAD_INIT(dead); + * + * When the event that needs to be caught occurs, {@link DEAD_KILL} is + * called, e.g.: + * DEAD_KILL(dead); + * The memory used by the dead variable is no longer needed after + * that. + * + * If a piece of code needs to know whether the event occured during some + * operation (but it must not have occured before!), it puts {@link DEAD_ENTER}} + * in front of the operation, and does {@link DEAD_LEAVE} at the end. If + * {@link DEAD_LEAVE} returned nonzero, the event occured, otherwise it did + * not. Example: + * DEAD_ENTER(dead) + * HandlerFunction(); + * if (DEAD_LEAVE(dead)) { + * (event occured) + * } + * + * If is is needed to check for the event more than once in a single block, + * {@link DEAD_DECLARE} should be put somewhere before, and {@link DEAD_ENTER2} + * should be used instead of {@link DEAD_ENTER}. + * + * If it is needed to check for multiple events (dead variables) at the same + * time, DEAD_*_N macros should be used instead, specifying different + * identiers as the first argument for different dead variables. + */ + +#ifndef BADVPN_MISC_DEAD_H +#define BADVPN_MISC_DEAD_H + +#include + +/** + * Dead variable. + */ +typedef int *dead_t; + +/** + * Initializes a dead variable. + */ +#define DEAD_INIT(ptr) { ptr = NULL; } + +/** + * Kills the dead variable, + */ +#define DEAD_KILL(ptr) { if (ptr) *(ptr) = 1; } + +/** + * Kills the dead variable with the given value, or does nothing + * if the value is 0. The value will seen by {@link DEAD_KILLED}. + */ +#define DEAD_KILL_WITH(ptr, val) { if (ptr) *(ptr) = (val); } + +/** + * Declares dead catching variables. + */ +#define DEAD_DECLARE int badvpn__dead; dead_t badvpn__prev_ptr; + +/** + * Enters a dead catching using already declared dead catching variables. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER2(ptr) { badvpn__dead = 0; badvpn__prev_ptr = ptr; ptr = &badvpn__dead; } + +/** + * Declares dead catching variables and enters a dead catching. + * The dead variable must have been initialized with {@link DEAD_INIT}, + * and {@link DEAD_KILL} must not have been called yet. + * {@link DEAD_LEAVE2} must be called before the current scope is left. + */ +#define DEAD_ENTER(ptr) DEAD_DECLARE DEAD_ENTER2(ptr) + +/** + * Leaves a dead catching. + */ +#define DEAD_LEAVE2(ptr) { if (!badvpn__dead) ptr = badvpn__prev_ptr; if (badvpn__prev_ptr) *badvpn__prev_ptr = badvpn__dead; } + +/** + * Returns 1 if {@link DEAD_KILL} was called for the dead variable, 0 otherwise. + * Must be called after entering a dead catching. + */ +#define DEAD_KILLED (badvpn__dead) + +#define DEAD_DECLARE_N(n) int badvpn__dead##n; dead_t badvpn__prev_ptr##n; +#define DEAD_ENTER2_N(n, ptr) { badvpn__dead##n = 0; badvpn__prev_ptr##n = ptr; ptr = &badvpn__dead##n;} +#define DEAD_ENTER_N(n, ptr) DEAD_DECLARE_N(n) DEAD_ENTER2_N(n, ptr) +#define DEAD_LEAVE2_N(n, ptr) { if (!badvpn__dead##n) ptr = badvpn__prev_ptr##n; if (badvpn__prev_ptr##n) *badvpn__prev_ptr##n = badvpn__dead##n; } +#define DEAD_KILLED_N(n) (badvpn__dead##n) + +#endif diff --git a/external/badvpn_dns/misc/debug.h b/external/badvpn_dns/misc/debug.h new file mode 100644 index 00000000..13bc866b --- /dev/null +++ b/external/badvpn_dns/misc/debug.h @@ -0,0 +1,142 @@ +/** + * @file debug.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Debugging macros. + */ + +/** + * @def DEBUG + * + * Macro for printing debugging text. Use the same way as printf, + * but without a newline. + * Prepends "function_name: " and appends a newline. + */ + +/** + * @def ASSERT_FORCE + * + * Macro for forced assertions. + * Evaluates the argument and terminates the program abnormally + * if the result is false. + */ + +/** + * @def ASSERT + * + * Macro for assertions. + * The argument may or may not be evaluated. + * If the argument is evaluated, it must not evaluate to false. + */ + +/** + * @def ASSERT_EXECUTE + * + * Macro for always-evaluated assertions. + * The argument is evaluated. + * The argument must not evaluate to false. + */ + +/** + * @def DEBUG_ZERO_MEMORY + * + * If debugging is enabled, zeroes the given memory region. + * First argument is pointer to the memory region, second is + * number of bytes. + */ + +/** + * @def WARN_UNUSED + * + * Tells the compiler that the result of a function should not be unused. + * Insert at the end of the declaration of a function before the semicolon. + */ + +/** + * @def B_USE + * + * This can be used to suppress warnings about unused variables. It can + * be applied to a variable or any expression. It does not evaluate the + * expression. + */ + +#ifndef BADVPN_MISC_DEBUG_H +#define BADVPN_MISC_DEBUG_H + +#include +#include +#include +#include +#include + +#define DEBUG(...) \ + { \ + fprintf(stderr, "%s: ", __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } + +#define ASSERT_FORCE(e) \ + { \ + if (!(e)) { \ + fprintf(stderr, "%s:%d Assertion failed\n", __FILE__, __LINE__); \ + abort(); \ + } \ + } + +#ifdef NDEBUG + #define DEBUG_ZERO_MEMORY(buf, len) {} + #define ASSERT(e) {} + #define ASSERT_EXECUTE(e) { (e); } +#else + #define DEBUG_ZERO_MEMORY(buf, len) { memset((buf), 0, (len)); } + #ifdef BADVPN_USE_C_ASSERT + #define ASSERT(e) { assert(e); } + #define ASSERT_EXECUTE(e) \ + { \ + int _assert_res = !!(e); \ + assert(_assert_res); \ + } + #else + #define ASSERT(e) ASSERT_FORCE(e) + #define ASSERT_EXECUTE(e) ASSERT_FORCE(e) + #endif +#endif + +#ifdef __GNUC__ + #define WARN_UNUSED __attribute__((warn_unused_result)) +#else + #define WARN_UNUSED +#endif + +#define B_USE(expr) (void)(sizeof((expr))); + +#define B_ASSERT_USE(expr) { ASSERT(expr) B_USE(expr) } + +#endif diff --git a/external/badvpn_dns/misc/debugcounter.h b/external/badvpn_dns/misc/debugcounter.h new file mode 100644 index 00000000..a8a54a13 --- /dev/null +++ b/external/badvpn_dns/misc/debugcounter.h @@ -0,0 +1,118 @@ +/** + * @file debugcounter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Counter for detecting leaks. + */ + +#ifndef BADVPN_MISC_DEBUGCOUNTER_H +#define BADVPN_MISC_DEBUGCOUNTER_H + +#include + +#include + +/** + * Counter for detecting leaks. + */ +typedef struct { +#ifndef NDEBUG + int32_t c; +#endif +} DebugCounter; + +#ifndef NDEBUG +#define DEBUGCOUNTER_STATIC { 0 } +#else +#define DEBUGCOUNTER_STATIC {} +#endif + +/** + * Initializes the object. + * The object is initialized with counter value zero. + * + * @param obj the object + */ +static void DebugCounter_Init (DebugCounter *obj) +{ +#ifndef NDEBUG + obj->c = 0; +#endif +} + +/** + * Frees the object. + * This does not have to be called when the counter is no longer needed. + * The counter value must be zero. + * + * @param obj the object + */ +static void DebugCounter_Free (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c == 0 || obj->c == INT32_MAX) +#endif +} + +/** + * Increments the counter. + * Increments the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Increment (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c >= 0) + + if (obj->c != INT32_MAX) { + obj->c++; + } +#endif +} + +/** + * Decrements the counter. + * The counter value must be >0. + * Decrements the counter value by one. + * + * @param obj the object + */ +static void DebugCounter_Decrement (DebugCounter *obj) +{ +#ifndef NDEBUG + ASSERT(obj->c > 0) + + if (obj->c != INT32_MAX) { + obj->c--; + } +#endif +} + +#endif diff --git a/external/badvpn_dns/misc/debugerror.h b/external/badvpn_dns/misc/debugerror.h new file mode 100644 index 00000000..182afd7b --- /dev/null +++ b/external/badvpn_dns/misc/debugerror.h @@ -0,0 +1,90 @@ +/** + * @file debugerror.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Mechanism for ensuring an object is destroyed from inside an error handler + * or its jobs. + */ + +#ifndef BADVPN_MISC_DEBUGERROR_H +#define BADVPN_MISC_DEBUGERROR_H + +#include +#include + +#ifndef NDEBUG + #define DEBUGERROR(de, call) \ + { \ + ASSERT(!BPending_IsSet(&(de)->job)) \ + BPending_Set(&(de)->job); \ + (call); \ + } +#else + #define DEBUGERROR(de, call) { (call); } +#endif + +typedef struct { + #ifndef NDEBUG + BPending job; + #endif +} DebugError; + +static void DebugError_Init (DebugError *o, BPendingGroup *pg); +static void DebugError_Free (DebugError *o); +static void DebugError_AssertNoError (DebugError *o); + +#ifndef NDEBUG +static void _DebugError_job_handler (DebugError *o) +{ + ASSERT(0); +} +#endif + +void DebugError_Init (DebugError *o, BPendingGroup *pg) +{ + #ifndef NDEBUG + BPending_Init(&o->job, pg, (BPending_handler)_DebugError_job_handler, o); + #endif +} + +void DebugError_Free (DebugError *o) +{ + #ifndef NDEBUG + BPending_Free(&o->job); + #endif +} + +void DebugError_AssertNoError (DebugError *o) +{ + #ifndef NDEBUG + ASSERT(!BPending_IsSet(&o->job)) + #endif +} + +#endif diff --git a/external/badvpn_dns/misc/dhcp_proto.h b/external/badvpn_dns/misc/dhcp_proto.h new file mode 100644 index 00000000..ed835445 --- /dev/null +++ b/external/badvpn_dns/misc/dhcp_proto.h @@ -0,0 +1,131 @@ +/** + * @file dhcp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the DHCP protocol. + */ + +#ifndef BADVPN_MISC_DHCP_PROTO_H +#define BADVPN_MISC_DHCP_PROTO_H + +#include + +#include + +#define DHCP_OP_BOOTREQUEST 1 +#define DHCP_OP_BOOTREPLY 2 + +#define DHCP_HARDWARE_ADDRESS_TYPE_ETHERNET 1 + +#define DHCP_MAGIC 0x63825363 + +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_END 255 + +#define DHCP_OPTION_SUBNET_MASK 1 +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DOMAIN_NAME_SERVER 6 +#define DHCP_OPTION_HOST_NAME 12 +#define DHCP_OPTION_REQUESTED_IP_ADDRESS 50 +#define DHCP_OPTION_IP_ADDRESS_LEASE_TIME 51 +#define DHCP_OPTION_DHCP_MESSAGE_TYPE 53 +#define DHCP_OPTION_DHCP_SERVER_IDENTIFIER 54 +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 +#define DHCP_OPTION_MAXIMUM_MESSAGE_SIZE 57 +#define DHCP_OPTION_RENEWAL_TIME_VALUE 58 +#define DHCP_OPTION_REBINDING_TIME_VALUE 59 +#define DHCP_OPTION_VENDOR_CLASS_IDENTIFIER 60 +#define DHCP_OPTION_CLIENT_IDENTIFIER 61 + +#define DHCP_MESSAGE_TYPE_DISCOVER 1 +#define DHCP_MESSAGE_TYPE_OFFER 2 +#define DHCP_MESSAGE_TYPE_REQUEST 3 +#define DHCP_MESSAGE_TYPE_DECLINE 4 +#define DHCP_MESSAGE_TYPE_ACK 5 +#define DHCP_MESSAGE_TYPE_NAK 6 +#define DHCP_MESSAGE_TYPE_RELEASE 7 + +B_START_PACKED +struct dhcp_header { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + uint32_t magic; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_header { + uint8_t type; + uint8_t len; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_dhcp_message_type { + uint8_t type; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_maximum_message_size { + uint16_t size; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_dhcp_server_identifier { + uint32_t id; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_time { + uint32_t time; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct dhcp_option_addr { + uint32_t addr; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/misc/ethernet_proto.h b/external/badvpn_dns/misc/ethernet_proto.h new file mode 100644 index 00000000..6e49e02b --- /dev/null +++ b/external/badvpn_dns/misc/ethernet_proto.h @@ -0,0 +1,52 @@ +/** + * @file ethernet_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the Ethernet protocol. + */ + +#ifndef BADVPN_MISC_ETHERNET_PROTO_H +#define BADVPN_MISC_ETHERNET_PROTO_H + +#include + +#include + +#define ETHERTYPE_IPV4 0x0800 +#define ETHERTYPE_ARP 0x0806 + +B_START_PACKED +struct ethernet_header { + uint8_t dest[6]; + uint8_t source[6]; + uint16_t type; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/misc/exparray.h b/external/badvpn_dns/misc/exparray.h new file mode 100644 index 00000000..b24149f8 --- /dev/null +++ b/external/badvpn_dns/misc/exparray.h @@ -0,0 +1,101 @@ +/** + * @file exparray.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dynamic array which grows exponentionally on demand. + */ + +#ifndef BADVPN_MISC_EXPARRAY_H +#define BADVPN_MISC_EXPARRAY_H + +#include +#include +#include + +#include + +struct ExpArray { + size_t esize; + size_t size; + void *v; +}; + +static int ExpArray_init (struct ExpArray *o, size_t esize, size_t size) +{ + ASSERT(esize > 0) + ASSERT(size > 0) + + o->esize = esize; + o->size = size; + + if (o->size > SIZE_MAX / o->esize) { + return 0; + } + + if (!(o->v = malloc(o->size * o->esize))) { + return 0; + } + + return 1; +} + +static int ExpArray_resize (struct ExpArray *o, size_t size) +{ + ASSERT(size > 0) + + if (size <= o->size) { + return 1; + } + + size_t newsize = o->size; + + while (newsize < size) { + if (2 > SIZE_MAX / newsize) { + return 0; + } + + newsize = 2 * newsize; + } + + if (newsize > SIZE_MAX / o->esize) { + return 0; + } + + void *newarr = realloc(o->v, newsize * o->esize); + if (!newarr) { + return 0; + } + + o->size = newsize; + o->v = newarr; + + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/expstring.h b/external/badvpn_dns/misc/expstring.h new file mode 100644 index 00000000..32471358 --- /dev/null +++ b/external/badvpn_dns/misc/expstring.h @@ -0,0 +1,161 @@ +/** + * @file expstring.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MISC_EXPSTRING_H +#define BADVPN_MISC_EXPSTRING_H + +#include + +#include +#include +#include + +typedef struct { + struct ExpArray arr; + size_t n; +} ExpString; + +static int ExpString_Init (ExpString *c); +static void ExpString_Free (ExpString *c); +static int ExpString_Append (ExpString *c, const char *str); +static int ExpString_AppendChar (ExpString *c, char ch); +static int ExpString_AppendByte (ExpString *c, uint8_t x); +static int ExpString_AppendBinary (ExpString *c, const uint8_t *data, size_t len); +static int ExpString_AppendZeros (ExpString *c, size_t len); +static char * ExpString_Get (ExpString *c); +static size_t ExpString_Length (ExpString *c); + +int ExpString_Init (ExpString *c) +{ + if (!ExpArray_init(&c->arr, 1, 16)) { + return 0; + } + + c->n = 0; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +void ExpString_Free (ExpString *c) +{ + free(c->arr.v); +} + +int ExpString_Append (ExpString *c, const char *str) +{ + ASSERT(str) + + size_t l = strlen(str); + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(l), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memcpy((char *)c->arr.v + c->n, str, l); + c->n += l; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendChar (ExpString *c, char ch) +{ + ASSERT(ch != '\0') + + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_fromint(2)); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + ((char *)c->arr.v)[c->n] = ch; + c->n++; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendByte (ExpString *c, uint8_t x) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_fromint(2)); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + ((uint8_t *)c->arr.v)[c->n] = x; + c->n++; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendBinary (ExpString *c, const uint8_t *data, size_t len) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(len), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memcpy((char *)c->arr.v + c->n, data, len); + c->n += len; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +int ExpString_AppendZeros (ExpString *c, size_t len) +{ + bsize_t newsize = bsize_add(bsize_fromsize(c->n), bsize_add(bsize_fromsize(len), bsize_fromint(1))); + + if (newsize.is_overflow || !ExpArray_resize(&c->arr, newsize.value)) { + return 0; + } + + memset((char *)c->arr.v + c->n, 0, len); + c->n += len; + ((char *)c->arr.v)[c->n] = '\0'; + + return 1; +} + +char * ExpString_Get (ExpString *c) +{ + return (char *)c->arr.v; +} + +size_t ExpString_Length (ExpString *c) +{ + return c->n; +} + +#endif diff --git a/external/badvpn_dns/misc/find_char.h b/external/badvpn_dns/misc/find_char.h new file mode 100644 index 00000000..9277ac69 --- /dev/null +++ b/external/badvpn_dns/misc/find_char.h @@ -0,0 +1,58 @@ +/** + * @file find_char.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_FIND_CHAR_H +#define BADVPN_FIND_CHAR_H + +#include + +#include + +/** + * Finds the first character 'c' in the string represented by 'str' and 'len'. + * If found, returns 1 and writes the position to *out_pos (if out_pos!=NULL). + * If not found, returns 0 and does not modify *out_pos. + */ +static int b_find_char_bin (const char *str, size_t len, char c, size_t *out_pos) +{ + ASSERT(str) + + for (size_t i = 0; i < len; i++) { + if (str[i] == c) { + if (out_pos) { + *out_pos = i; + } + return 1; + } + } + + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/find_program.h b/external/badvpn_dns/misc/find_program.h new file mode 100644 index 00000000..ecc87be5 --- /dev/null +++ b/external/badvpn_dns/misc/find_program.h @@ -0,0 +1,103 @@ +/** + * @file find_program.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function that finds the absolute path of a program by checking a predefined + * list of directories. + */ + +#ifndef BADVPN_FIND_PROGRAM_H +#define BADVPN_FIND_PROGRAM_H + +#include +#include +#include + +#include +#include +#include + +static char * badvpn_find_program (const char *name); + +static char * badvpn_find_program (const char *name) +{ + ASSERT(name) + + char *path = getenv("PATH"); + if (path) { + while (1) { + size_t i = 0; + while (path[i] != ':' && path[i] != '\0') { + i++; + } + char const *src = path; + size_t src_len = i; + if (src_len == 0) { + src = "."; + src_len = 1; + } + size_t name_len = strlen(name); + char *entry = BAllocSize(bsize_add(bsize_fromsize(src_len), bsize_add(bsize_fromsize(name_len), bsize_fromsize(2)))); + if (!entry) { + goto fail; + } + memcpy(entry, src, src_len); + entry[src_len] = '/'; + strcpy(entry + (src_len + 1), name); + if (access(entry, X_OK) == 0) { + return entry; + } + free(entry); + if (path[i] == '\0') { + break; + } + path += i + 1; + } + } + + const char *dirs[] = {"/usr/sbin", "/usr/bin", "/sbin", "/bin", NULL}; + + for (size_t i = 0; dirs[i]; i++) { + char *path = concat_strings(3, dirs[i], "/", name); + if (!path) { + goto fail; + } + + if (access(path, X_OK) == 0) { + return path; + } + + free(path); + } + +fail: + return NULL; +} + +#endif diff --git a/external/badvpn_dns/misc/get_iface_info.h b/external/badvpn_dns/misc/get_iface_info.h new file mode 100644 index 00000000..190741b3 --- /dev/null +++ b/external/badvpn_dns/misc/get_iface_info.h @@ -0,0 +1,110 @@ +/** + * @file get_iface_info.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_GETIFACEINFO_H +#define BADVPN_GETIFACEINFO_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Returns information about a network interface with the given name. + * + * @param ifname name of interface to get information for + * @param out_mac the MAC address will be returned here, unless NULL + * @param out_mtu the MTU will be returned here, unless NULL + * @param out_ifindex the interface index will be returned here, unless NULL + * @return 1 on success, 0 on failure + */ +static int badvpn_get_iface_info (const char *ifname, uint8_t *out_mac, int *out_mtu, int *out_ifindex) WARN_UNUSED; + + +static int badvpn_get_iface_info (const char *ifname, uint8_t *out_mac, int *out_mtu, int *out_ifindex) +{ + ASSERT(ifname) + + struct ifreq ifr; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + goto fail0; + } + + // get MAC + if (out_mac) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFHWADDR, &ifr)) { + goto fail1; + } + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + goto fail1; + } + memcpy(out_mac, ifr.ifr_hwaddr.sa_data, 6); + } + + // get MTU + if (out_mtu) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFMTU, &ifr)) { + goto fail1; + } + *out_mtu = ifr.ifr_mtu; + } + + // get interface index + if (out_ifindex) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFINDEX, &ifr)) { + goto fail1; + } + *out_ifindex = ifr.ifr_ifindex; + } + + close(s); + + return 1; + +fail1: + close(s); +fail0: + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/grow_array.h b/external/badvpn_dns/misc/grow_array.h new file mode 100644 index 00000000..9a42d04c --- /dev/null +++ b/external/badvpn_dns/misc/grow_array.h @@ -0,0 +1,139 @@ +/** + * @file grow_array.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// GROWARRAY_NAME - prefix of of functions to define +// GROWARRAY_OBJECT_TYPE - type of structure where array and capacity sizes are +// GROWARRAY_ARRAY_MEMBER - array member +// GROWARRAY_CAPACITY_MEMBER - capacity member +// GROWARRAY_MAX_CAPACITY - max value of capacity member + +#include +#include +#include + +#include +#include +#include + +#define GrowArrayObject GROWARRAY_OBJECT_TYPE +#define GrowArray_Init MERGE(GROWARRAY_NAME, _Init) +#define GrowArray_InitEmpty MERGE(GROWARRAY_NAME, _InitEmpty) +#define GrowArray_Free MERGE(GROWARRAY_NAME, _Free) +#define GrowArray_DoubleUp MERGE(GROWARRAY_NAME, _DoubleUp) +#define GrowArray_DoubleUpLimit MERGE(GROWARRAY_NAME, _DoubleUpLimit) + +static int GrowArray_Init (GrowArrayObject *o, size_t capacity) WARN_UNUSED; +static void GrowArray_InitEmpty (GrowArrayObject *o); +static void GrowArray_Free (GrowArrayObject *o); +static int GrowArray_DoubleUp (GrowArrayObject *o) WARN_UNUSED; +static int GrowArray_DoubleUpLimit (GrowArrayObject *o, size_t limit) WARN_UNUSED; + +static int GrowArray_Init (GrowArrayObject *o, size_t capacity) +{ + if (capacity > GROWARRAY_MAX_CAPACITY) { + return 0; + } + + if (capacity == 0) { + o->GROWARRAY_ARRAY_MEMBER = NULL; + } else { + if (!(o->GROWARRAY_ARRAY_MEMBER = BAllocArray(capacity, sizeof(o->GROWARRAY_ARRAY_MEMBER[0])))) { + return 0; + } + } + + o->GROWARRAY_CAPACITY_MEMBER = capacity; + + return 1; +} + +static void GrowArray_InitEmpty (GrowArrayObject *o) +{ + o->GROWARRAY_ARRAY_MEMBER = NULL; + o->GROWARRAY_CAPACITY_MEMBER = 0; +} + +static void GrowArray_Free (GrowArrayObject *o) +{ + if (o->GROWARRAY_ARRAY_MEMBER) { + BFree(o->GROWARRAY_ARRAY_MEMBER); + } +} + +static int GrowArray_DoubleUp (GrowArrayObject *o) +{ + return GrowArray_DoubleUpLimit(o, SIZE_MAX); +} + +static int GrowArray_DoubleUpLimit (GrowArrayObject *o, size_t limit) +{ + if (o->GROWARRAY_CAPACITY_MEMBER > SIZE_MAX / 2 || o->GROWARRAY_CAPACITY_MEMBER > GROWARRAY_MAX_CAPACITY / 2) { + return 0; + } + + size_t newcap = 2 * o->GROWARRAY_CAPACITY_MEMBER; + if (newcap == 0) { + newcap = 1; + } + + if (newcap > limit) { + newcap = limit; + if (newcap == o->GROWARRAY_CAPACITY_MEMBER) { + return 0; + } + } + + void *newarr = BAllocArray(newcap, sizeof(o->GROWARRAY_ARRAY_MEMBER[0])); + if (!newarr) { + return 0; + } + + memcpy(newarr, o->GROWARRAY_ARRAY_MEMBER, o->GROWARRAY_CAPACITY_MEMBER * sizeof(o->GROWARRAY_ARRAY_MEMBER[0])); + + BFree(o->GROWARRAY_ARRAY_MEMBER); + + o->GROWARRAY_ARRAY_MEMBER = newarr; + o->GROWARRAY_CAPACITY_MEMBER = newcap; + + return 1; +} + +#undef GROWARRAY_NAME +#undef GROWARRAY_OBJECT_TYPE +#undef GROWARRAY_ARRAY_MEMBER +#undef GROWARRAY_CAPACITY_MEMBER +#undef GROWARRAY_MAX_CAPACITY + +#undef GrowArrayObject +#undef GrowArray_Init +#undef GrowArray_InitEmpty +#undef GrowArray_Free +#undef GrowArray_DoubleUp +#undef GrowArray_DoubleUpLimit diff --git a/external/badvpn_dns/misc/hashfun.h b/external/badvpn_dns/misc/hashfun.h new file mode 100644 index 00000000..5e8956ab --- /dev/null +++ b/external/badvpn_dns/misc/hashfun.h @@ -0,0 +1,60 @@ +/** + * @file hashfun.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_HASHFUN_H +#define BADVPN_HASHFUN_H + +#include +#include + +static size_t badvpn_djb2_hash (const uint8_t *str) +{ + size_t hash = 5381; + int c; + + while (c = *str++) { + hash = ((hash << 5) + hash) + c; + } + + return hash; +} + +static size_t badvpn_djb2_hash_bin (const uint8_t *str, size_t str_len) +{ + size_t hash = 5381; + + while (str_len-- > 0) { + int c = *str++; + hash = ((hash << 5) + hash) + c; + } + + return hash; +} + +#endif diff --git a/external/badvpn_dns/misc/igmp_proto.h b/external/badvpn_dns/misc/igmp_proto.h new file mode 100644 index 00000000..9188ea02 --- /dev/null +++ b/external/badvpn_dns/misc/igmp_proto.h @@ -0,0 +1,97 @@ +/** + * @file igmp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the IGMP protocol. + */ + +#ifndef BADVPN_MISC_IGMP_PROTO_H +#define BADVPN_MISC_IGMP_PROTO_H + +#include + +#include + +#define IGMP_TYPE_MEMBERSHIP_QUERY 0x11 +#define IGMP_TYPE_V1_MEMBERSHIP_REPORT 0x12 +#define IGMP_TYPE_V2_MEMBERSHIP_REPORT 0x16 +#define IGMP_TYPE_V3_MEMBERSHIP_REPORT 0x22 +#define IGMP_TYPE_V2_LEAVE_GROUP 0x17 + +#define IGMP_RECORD_TYPE_MODE_IS_INCLUDE 1 +#define IGMP_RECORD_TYPE_MODE_IS_EXCLUDE 2 +#define IGMP_RECORD_TYPE_CHANGE_TO_INCLUDE_MODE 3 +#define IGMP_RECORD_TYPE_CHANGE_TO_EXCLUDE_MODE 4 + +B_START_PACKED +struct igmp_source { + uint32_t addr; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_base { + uint8_t type; + uint8_t max_resp_code; + uint16_t checksum; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_query_extra { + uint32_t group; + uint8_t reserved4_suppress1_qrv3; + uint8_t qqic; + uint16_t number_of_sources; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_report_extra { + uint16_t reserved; + uint16_t number_of_group_records; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v3_report_record { + uint8_t type; + uint8_t aux_data_len; + uint16_t number_of_sources; + uint32_t group; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct igmp_v2_extra { + uint32_t group; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/misc/ipaddr.h b/external/badvpn_dns/misc/ipaddr.h new file mode 100644 index 00000000..8c7cb052 --- /dev/null +++ b/external/badvpn_dns/misc/ipaddr.h @@ -0,0 +1,218 @@ +/** + * @file ipaddr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IP address parsing functions. + */ + +#ifndef BADVPN_MISC_IPADDR_H +#define BADVPN_MISC_IPADDR_H + +#include +#include + +#include +#include +#include +#include +#include + +struct ipv4_ifaddr { + uint32_t addr; + int prefix; +}; + +static int ipaddr_parse_ipv4_addr_bin (const char *name, size_t name_len, uint32_t *out_addr); +static int ipaddr_parse_ipv4_addr (const char *name, uint32_t *out_addr); +static int ipaddr_parse_ipv4_prefix_bin (const char *str, size_t str_len, int *num); +static int ipaddr_parse_ipv4_prefix (const char *str, int *num); +static int ipaddr_parse_ipv4_ifaddr_bin (const char *str, size_t str_len, struct ipv4_ifaddr *out); +static int ipaddr_parse_ipv4_ifaddr (const char *str, struct ipv4_ifaddr *out); +static int ipaddr_ipv4_ifaddr_from_addr_mask (uint32_t addr, uint32_t mask, struct ipv4_ifaddr *out); +static uint32_t ipaddr_ipv4_mask_from_prefix (int prefix); +static int ipaddr_ipv4_prefix_from_mask (uint32_t mask, int *out_prefix); +static int ipaddr_ipv4_addrs_in_network (uint32_t addr1, uint32_t addr2, int netprefix); + +#define IPADDR_PRINT_MAX 19 + +static void ipaddr_print_addr (uint32_t addr, char *out); +static void ipaddr_print_ifaddr (struct ipv4_ifaddr ifaddr, char *out); + +int ipaddr_parse_ipv4_addr_bin (const char *name, size_t name_len, uint32_t *out_addr) +{ + for (size_t i = 0; ; i++) { + size_t j; + for (j = 0; j < name_len && name[j] != '.'; j++); + + if ((j == name_len && i < 3) || (j < name_len && i == 3)) { + return 0; + } + + if (j < 1 || j > 3) { + return 0; + } + + uintmax_t d; + if (!parse_unsigned_integer_bin(name, j, &d)) { + return 0; + } + + if (d > 255) { + return 0; + } + + ((uint8_t *)out_addr)[i] = d; + + if (i == 3) { + return 1; + } + + name += j + 1; + name_len -= j + 1; + } +} + +int ipaddr_parse_ipv4_addr (const char *name, uint32_t *out_addr) +{ + return ipaddr_parse_ipv4_addr_bin(name, strlen(name), out_addr); +} + +int ipaddr_parse_ipv4_prefix_bin (const char *str, size_t str_len, int *num) +{ + uintmax_t d; + if (!parse_unsigned_integer_bin(str, str_len, &d)) { + return 0; + } + if (d > 32) { + return 0; + } + + *num = d; + return 1; +} + +int ipaddr_parse_ipv4_prefix (const char *str, int *num) +{ + return ipaddr_parse_ipv4_prefix_bin(str, strlen(str), num); +} + +int ipaddr_parse_ipv4_ifaddr_bin (const char *str, size_t str_len, struct ipv4_ifaddr *out) +{ + size_t slash_pos; + if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { + return 0; + } + + return (ipaddr_parse_ipv4_addr_bin(str, slash_pos, &out->addr) && + ipaddr_parse_ipv4_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); +} + +int ipaddr_parse_ipv4_ifaddr (const char *str, struct ipv4_ifaddr *out) +{ + return ipaddr_parse_ipv4_ifaddr_bin(str, strlen(str), out); +} + +int ipaddr_ipv4_ifaddr_from_addr_mask (uint32_t addr, uint32_t mask, struct ipv4_ifaddr *out) +{ + int prefix; + if (!ipaddr_ipv4_prefix_from_mask(mask, &prefix)) { + return 0; + } + + out->addr = addr; + out->prefix = prefix; + return 1; +} + +uint32_t ipaddr_ipv4_mask_from_prefix (int prefix) +{ + ASSERT(prefix >= 0) + ASSERT(prefix <= 32) + + uint32_t t = 0; + for (int i = 0; i < prefix; i++) { + t |= 1 << (32 - i - 1); + } + + return hton32(t); +} + +int ipaddr_ipv4_prefix_from_mask (uint32_t mask, int *out_prefix) +{ + uint32_t t = 0; + int i; + for (i = 0; i <= 32; i++) { + if (ntoh32(mask) == t) { + break; + } + if (i < 32) { + t |= (1 << (32 - i - 1)); + } + } + if (!(i <= 32)) { + return 0; + } + + *out_prefix = i; + return 1; +} + +int ipaddr_ipv4_addrs_in_network (uint32_t addr1, uint32_t addr2, int netprefix) +{ + ASSERT(netprefix >= 0) + ASSERT(netprefix <= 32) + + uint32_t mask = ipaddr_ipv4_mask_from_prefix(netprefix); + + return !!((addr1 & mask) == (addr2 & mask)); +} + +void ipaddr_print_addr (uint32_t addr, char *out) +{ + ASSERT(out) + + uint8_t *b = (uint8_t *)&addr; + + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, + b[0], b[1], b[2], b[3]); +} + +void ipaddr_print_ifaddr (struct ipv4_ifaddr ifaddr, char *out) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + ASSERT(out) + + uint8_t *b = (uint8_t *)&ifaddr.addr; + + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d", + b[0], b[1], b[2], b[3], ifaddr.prefix); +} + +#endif diff --git a/external/badvpn_dns/misc/ipaddr6.h b/external/badvpn_dns/misc/ipaddr6.h new file mode 100644 index 00000000..ebacd94b --- /dev/null +++ b/external/badvpn_dns/misc/ipaddr6.h @@ -0,0 +1,400 @@ +/** + * @file ipaddr6.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 address parsing functions. + */ + +#ifndef BADVPN_MISC_IPADDR6_H +#define BADVPN_MISC_IPADDR6_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct ipv6_addr { + uint8_t bytes[16]; +}; + +struct ipv6_ifaddr { + struct ipv6_addr addr; + int prefix; +}; + +static int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr); +static int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num); +static int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num); +static int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out); +static int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out); +static int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out); +static void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask); +static int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix); +static int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix); + +#define IPADDR6_PRINT_MAX 44 + +static void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf); +static void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf); + +int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr) +{ + int num_blocks = 0; + int compress_pos = -1; + uint16_t block = 0; + int empty = 1; + + size_t i = 0; + + while (i < name_len) { + if (name[i] == '.') { + goto ipv4_ending; + } else if (name[i] == ':') { + int is_double = (i + 1 < name_len && name[i + 1] == ':'); + + if (i > 0) { + if (empty || num_blocks == 7) { + return 0; + } + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + block = 0; + empty = 1; + } + else if (!is_double) { + return 0; + } + + if (is_double) { + if (compress_pos != -1) { + return 0; + } + compress_pos = num_blocks; + } + + i += 1 + is_double; + } else { + int digit = decode_hex_digit(name[i]); + if (digit < 0) { + return 0; + } + if (block > UINT16_MAX / 16) { + return 0; + } + block *= 16; + if (digit > UINT16_MAX - block) { + return 0; + } + block += digit; + empty = 0; + i += 1; + } + } + + if (!empty) { + out_addr->bytes[2 * num_blocks + 0] = block >> 8; + out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; + num_blocks++; + } + else if (num_blocks != compress_pos) { + return 0; + } + +ipv4_done: + if (compress_pos == -1) { + if (num_blocks != 8) { + return 0; + } + compress_pos = 0; + } + + int num_rear = num_blocks - compress_pos; + memmove(out_addr->bytes + 2 * (8 - num_rear), out_addr->bytes + 2 * compress_pos, 2 * num_rear); + memset(out_addr->bytes + 2 * compress_pos, 0, 2 * (8 - num_rear - compress_pos)); + + return 1; + +ipv4_ending: + if (empty || (num_blocks == 0 && compress_pos == -1)) { + return 0; + } + + while (name[i - 1] != ':') { + i--; + } + + uint8_t bytes[4]; + int cur_byte = 0; + uint8_t byte = 0; + empty = 1; + + while (i < name_len) { + if (name[i] == '.') { + if (empty || cur_byte == 3) { + return 0; + } + bytes[cur_byte] = byte; + cur_byte++; + byte = 0; + empty = 1; + } else { + if (!empty && byte == 0) { + return 0; + } + int digit = decode_decimal_digit(name[i]); + if (digit < 0) { + return 0; + } + if (byte > UINT8_MAX / 10) { + return 0; + } + byte *= 10; + if (digit > UINT8_MAX - byte) { + return 0; + } + byte += digit; + empty = 0; + } + i++; + } + + if (cur_byte != 3 || empty) { + return 0; + } + bytes[cur_byte] = byte; + + if (8 - num_blocks < 2) { + return 0; + } + memcpy(out_addr->bytes + 2 * num_blocks, bytes, 4); + num_blocks += 2; + + goto ipv4_done; +} + +int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr) +{ + return ipaddr6_parse_ipv6_addr_bin(name, strlen(name), out_addr); +} + +int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num) +{ + uintmax_t d; + if (!parse_unsigned_integer_bin(str, str_len, &d)) { + return 0; + } + if (d > 128) { + return 0; + } + + *out_num = d; + return 1; +} + +int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num) +{ + return ipaddr6_parse_ipv6_prefix_bin(str, strlen(str), out_num); +} + +int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out) +{ + size_t slash_pos; + if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { + return 0; + } + + return (ipaddr6_parse_ipv6_addr_bin(str, slash_pos, &out->addr) && + ipaddr6_parse_ipv6_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); +} + +int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out) +{ + return ipaddr6_parse_ipv6_ifaddr_bin(str, strlen(str), out); +} + +int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out) +{ + int prefix; + if (!ipaddr6_ipv6_prefix_from_mask(mask, &prefix)) { + return 0; + } + + out->addr = addr; + out->prefix = prefix; + return 1; +} + +void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask) +{ + ASSERT(prefix >= 0) + ASSERT(prefix <= 128) + + int quot = prefix / 8; + int rem = prefix % 8; + + if (quot > 0) { + memset(out_mask->bytes, UINT8_MAX, quot); + } + if (16 - quot > 0) { + memset(out_mask->bytes + quot, 0, 16 - quot); + } + + for (int i = 0; i < rem; i++) { + out_mask->bytes[quot] |= (uint8_t)1 << (8 - i - 1); + } +} + +int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix) +{ + int prefix = 0; + int i = 0; + + while (i < 16 && mask.bytes[i] == UINT8_MAX) { + prefix += 8; + i++; + } + + if (i < 16) { + uint8_t t = 0; + int j; + for (j = 0; j <= 8; j++) { + if (mask.bytes[i] == t) { + break; + } + if (j < 8) { + t |= ((uint8_t)1 << (8 - j - 1)); + } + } + if (!(j <= 8)) { + return 0; + } + + prefix += j; + i++; + + while (i < 16) { + if (mask.bytes[i] != 0) { + return 0; + } + i++; + } + } + + *out_prefix = prefix; + return 1; +} + +int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix) +{ + ASSERT(netprefix >= 0) + ASSERT(netprefix <= 128) + + int quot = netprefix / 8; + int rem = netprefix % 8; + + if (memcmp(addr1.bytes, addr2.bytes, quot)) { + return 0; + } + + if (rem == 0) { + return 1; + } + + uint8_t t = 0; + for (int i = 0; i < rem; i++) { + t |= (uint8_t)1 << (8 - i - 1); + } + + return ((addr1.bytes[quot] & t) == (addr2.bytes[quot] & t)); +} + +void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf) +{ + int largest_start = 0; + int largest_len = 0; + int current_start = 0; + int current_len = 0; + + for (int i = 0; i < 8; i++) { + if (addr.bytes[2 * i] == 0 && addr.bytes[2 * i + 1] == 0) { + current_len++; + if (current_len > largest_len) { + largest_start = current_start; + largest_len = current_len; + } + } else { + current_start = i + 1; + current_len = 0; + } + } + + if (largest_len > 1) { + for (int i = 0; i < largest_start; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%"PRIx16":", block); + } + if (largest_start == 0) { + out_buf += sprintf(out_buf, ":"); + } + + for (int i = largest_start + largest_len; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, ":%"PRIx16, block); + } + if (largest_start + largest_len == 8) { + out_buf += sprintf(out_buf, ":"); + } + } else { + const char *prefix = ""; + for (int i = 0; i < 8; i++) { + uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; + out_buf += sprintf(out_buf, "%s%"PRIx16, prefix, block); + prefix = ":"; + } + } +} + +void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf) +{ + ASSERT(addr.prefix >= 0) + ASSERT(addr.prefix <= 128) + + ipaddr6_print_addr(addr.addr, out_buf); + sprintf(out_buf + strlen(out_buf), "/%d", addr.prefix); +} + +#endif diff --git a/external/badvpn_dns/misc/ipv4_proto.h b/external/badvpn_dns/misc/ipv4_proto.h new file mode 100644 index 00000000..fea72601 --- /dev/null +++ b/external/badvpn_dns/misc/ipv4_proto.h @@ -0,0 +1,145 @@ +/** + * @file ipv4_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the IPv4 protocol. + */ + +#ifndef BADVPN_MISC_IPV4_PROTO_H +#define BADVPN_MISC_IPV4_PROTO_H + +#include +#include + +#include +#include +#include +#include + +#define IPV4_PROTOCOL_IGMP 2 +#define IPV4_PROTOCOL_UDP 17 + +B_START_PACKED +struct ipv4_header { + uint8_t version4_ihl4; + uint8_t ds; + uint16_t total_length; + // + uint16_t identification; + uint16_t flags3_fragmentoffset13; + // + uint8_t ttl; + uint8_t protocol; + uint16_t checksum; + // + uint32_t source_address; + // + uint32_t destination_address; +} B_PACKED; +B_END_PACKED + +#define IPV4_GET_VERSION(_header) (((_header).version4_ihl4&0xF0)>>4) +#define IPV4_GET_IHL(_header) (((_header).version4_ihl4&0x0F)>>0) + +#define IPV4_MAKE_VERSION_IHL(size) (((size)/4) + (4 << 4)) + +static uint16_t ipv4_checksum (const struct ipv4_header *header, const char *extra, uint16_t extra_len) +{ + ASSERT(extra_len % 2 == 0) + ASSERT(extra_len == 0 || extra) + + uint32_t t = 0; + + for (uint16_t i = 0; i < sizeof(*header) / 2; i++) { + t += badvpn_read_be16((const char *)header + 2 * i); + } + + for (uint16_t i = 0; i < extra_len / 2; i++) { + t += badvpn_read_be16((const char *)extra + 2 * i); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + return hton16(~t); +} + +static int ipv4_check (uint8_t *data, int data_len, struct ipv4_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv4_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if (IPV4_GET_VERSION(*out_header) != 4) { + return 0; + } + + // check options + uint16_t header_len = IPV4_GET_IHL(*out_header) * 4; + if (header_len < sizeof(struct ipv4_header)) { + return 0; + } + if (header_len > data_len) { + return 0; + } + + // check total length + uint16_t total_length = ntoh16(out_header->total_length); + if (total_length < header_len) { + return 0; + } + if (total_length > data_len) { + return 0; + } + + // check checksum + uint16_t checksum_in_packet = out_header->checksum; + out_header->checksum = hton16(0); + uint16_t checksum_computed = ipv4_checksum(out_header, (char *)data + sizeof(*out_header), header_len - sizeof(*out_header)); + out_header->checksum = checksum_in_packet; + if (checksum_in_packet != checksum_computed) { + return 0; + } + + *out_payload = data + header_len; + *out_payload_len = total_length - header_len; + + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/ipv6_proto.h b/external/badvpn_dns/misc/ipv6_proto.h new file mode 100644 index 00000000..b2555413 --- /dev/null +++ b/external/badvpn_dns/misc/ipv6_proto.h @@ -0,0 +1,86 @@ +/** + * @file ipv6_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_IPV6_PROTO_H +#define BADVPN_IPV6_PROTO_H + +#include +#include + +#include +#include +#include + +#define IPV6_NEXT_IGMP 2 +#define IPV6_NEXT_UDP 17 + +B_START_PACKED +struct ipv6_header { + uint8_t version4_tc4; + uint8_t tc4_fl4; + uint16_t fl; + uint16_t payload_length; + uint8_t next_header; + uint8_t hop_limit; + uint8_t source_address[16]; + uint8_t destination_address[16]; +} B_PACKED; +B_END_PACKED + +static int ipv6_check (uint8_t *data, int data_len, struct ipv6_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // check base header + if (data_len < sizeof(struct ipv6_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + + // check version + if ((ntoh8(out_header->version4_tc4) >> 4) != 6) { + return 0; + } + + // check payload length + uint16_t payload_length = ntoh16(out_header->payload_length); + if (payload_length > data_len - sizeof(struct ipv6_header)) { + return 0; + } + + *out_payload = data + sizeof(struct ipv6_header); + *out_payload_len = payload_length; + + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/loggers_string.h b/external/badvpn_dns/misc/loggers_string.h new file mode 100644 index 00000000..66986b84 --- /dev/null +++ b/external/badvpn_dns/misc/loggers_string.h @@ -0,0 +1,43 @@ +/** + * @file loggers_string.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * List of available loggers. + */ + +#ifndef BADVPN_MISC_LOGGERSSTRING_H +#define BADVPN_MISC_LOGGERSSTRING_H + +#ifdef BADVPN_USE_WINAPI +#define LOGGERS_STRING "stdout" +#else +#define LOGGERS_STRING "stdout/syslog" +#endif + +#endif diff --git a/external/badvpn_dns/misc/loglevel.h b/external/badvpn_dns/misc/loglevel.h new file mode 100644 index 00000000..6e9f911d --- /dev/null +++ b/external/badvpn_dns/misc/loglevel.h @@ -0,0 +1,80 @@ +/** + * @file loglevel.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Log level specification parsing function. + */ + +#ifndef BADVPN_MISC_LOGLEVEL_H +#define BADVPN_MISC_LOGLEVEL_H + +#include + +#include + +/** + * Parses the log level string. + * + * @param str log level string. Recognizes none, error, warning, notice, + * info, debug. + * @return 0 for none, one of BLOG_* for some log level, -1 for unrecognized + */ +static int parse_loglevel (char *str); + +int parse_loglevel (char *str) +{ + if (!strcmp(str, "none")) { + return 0; + } + if (!strcmp(str, "error")) { + return BLOG_ERROR; + } + if (!strcmp(str, "warning")) { + return BLOG_WARNING; + } + if (!strcmp(str, "notice")) { + return BLOG_NOTICE; + } + if (!strcmp(str, "info")) { + return BLOG_INFO; + } + if (!strcmp(str, "debug")) { + return BLOG_DEBUG; + } + + char *endptr; + long int res = strtol(str, &endptr, 10); + if (*str && !*endptr && res >= 0 && res <= BLOG_DEBUG) { + return res; + } + + return -1; +} + +#endif diff --git a/external/badvpn_dns/misc/maxalign.h b/external/badvpn_dns/misc/maxalign.h new file mode 100644 index 00000000..cb1f4605 --- /dev/null +++ b/external/badvpn_dns/misc/maxalign.h @@ -0,0 +1,53 @@ +/** + * @file maxalign.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MAXALIGN_H +#define BADVPN_MAXALIGN_H + +#include +#include + +typedef union { + short a; + long b; + long long c; + double d; + long double e; + void *f; + uint8_t g; + uint16_t h; + uint32_t i; + uint64_t j; + size_t k; + void (*l) (void); +} bmax_align_t; + +#define BMAX_ALIGN (__alignof(bmax_align_t)) + +#endif diff --git a/external/badvpn_dns/misc/merge.h b/external/badvpn_dns/misc/merge.h new file mode 100644 index 00000000..53227712 --- /dev/null +++ b/external/badvpn_dns/misc/merge.h @@ -0,0 +1,36 @@ +/** + * @file merge.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MERGE_H +#define BADVPN_MERGE_H + +#define MERGE_HELPER(x, y) x ## y +#define MERGE(x, y) MERGE_HELPER(x, y) + +#endif diff --git a/external/badvpn_dns/misc/minmax.h b/external/badvpn_dns/misc/minmax.h new file mode 100644 index 00000000..ca82055e --- /dev/null +++ b/external/badvpn_dns/misc/minmax.h @@ -0,0 +1,56 @@ +/** + * @file minmax.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Minimum and maximum macros. + */ + +#ifndef BADVPN_MISC_MINMAX_H +#define BADVPN_MISC_MINMAX_H + +#include +#include + +#define DEFINE_BMINMAX(name, type) \ +static type bmin ## name (type a, type b) { return (a < b ? a : b); } \ +static type bmax ## name (type a, type b) { return (a > b ? a : b); } + +DEFINE_BMINMAX(_size, size_t) +DEFINE_BMINMAX(_int, int) +DEFINE_BMINMAX(_int8, int8_t) +DEFINE_BMINMAX(_int16, int16_t) +DEFINE_BMINMAX(_int32, int32_t) +DEFINE_BMINMAX(_int64, int64_t) +DEFINE_BMINMAX(_uint, unsigned int) +DEFINE_BMINMAX(_uint8, uint8_t) +DEFINE_BMINMAX(_uint16, uint16_t) +DEFINE_BMINMAX(_uint32, uint32_t) +DEFINE_BMINMAX(_uint64, uint64_t) + +#endif diff --git a/external/badvpn_dns/misc/modadd.h b/external/badvpn_dns/misc/modadd.h new file mode 100644 index 00000000..4e4f04ad --- /dev/null +++ b/external/badvpn_dns/misc/modadd.h @@ -0,0 +1,59 @@ +/** + * @file modadd.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Modular addition macro. + * + * Calculates (x + y) mod m, assuming + * 0 <= x < m and 0 <= y < m. + */ + +#ifndef BADVPN_MISC_MODADD_H +#define BADVPN_MISC_MODADD_H + +#include + +#define DECLARE_BMODADD(type, name) \ +static type bmodadd_##name (type x, type y, type m) \ +{ \ + ASSERT(x >= 0) \ + ASSERT(x < m) \ + ASSERT(y >= 0) \ + ASSERT(y < m) \ + \ + if (y >= m - x) { \ + return (y - (m - x)); \ + } else { \ + return (x + y); \ + } \ +} \ + +DECLARE_BMODADD(int, int) + +#endif diff --git a/external/badvpn_dns/misc/mswsock.h b/external/badvpn_dns/misc/mswsock.h new file mode 100644 index 00000000..4f4c38df --- /dev/null +++ b/external/badvpn_dns/misc/mswsock.h @@ -0,0 +1,229 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include + +#ifndef _MSWSOCK_ +#define _MSWSOCK_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SO_CONNDATA 0x7000 +#define SO_CONNOPT 0x7001 +#define SO_DISCDATA 0x7002 +#define SO_DISCOPT 0x7003 +#define SO_CONNDATALEN 0x7004 +#define SO_CONNOPTLEN 0x7005 +#define SO_DISCDATALEN 0x7006 +#define SO_DISCOPTLEN 0x7007 + +#define SO_OPENTYPE 0x7008 + +#define SO_SYNCHRONOUS_ALERT 0x10 +#define SO_SYNCHRONOUS_NONALERT 0x20 + +#define SO_MAXDG 0x7009 +#define SO_MAXPATHDG 0x700A +#define SO_UPDATE_ACCEPT_CONTEXT 0x700B +#define SO_CONNECT_TIME 0x700C +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 + +#define TCP_BSDURGENT 0x7000 + +#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) +#if (_WIN32_WINNT < 0x0600) && (_WIN32_WINNT >= 0x0501) +#define SIO_SOCKET_CLOSE_NOTIFY _WSAIOW(IOC_VENDOR,13) +#endif /* >= XP && < VISTA */ +#if (_WIN32_WINNT >= 0x0600) +#define SIO_BSP_HANDLE _WSAIOR(IOC_WS2,27) +#define SIO_BSP_HANDLE_SELECT _WSAIOR(IOC_WS2,28) +#define SIO_BSP_HANDLE_POLL _WSAIOR(IOC_WS2,29) + +#define SIO_EXT_SELECT _WSAIORW(IOC_WS2,30) +#define SIO_EXT_POLL _WSAIORW(IOC_WS2,31) +#define SIO_EXT_SENDMSG _WSAIORW(IOC_WS2,32) + +#define SIO_BASE_HANDLE _WSAIOR(IOC_WS2,34) +#endif /* _WIN32_WINNT >= 0x0600 */ + +#ifndef __MSWSOCK_WS1_SHARED + int WINAPI WSARecvEx(SOCKET s,char *buf,int len,int *flags); +#endif /* __MSWSOCK_WS1_SHARED */ + +#define TF_DISCONNECT 0x01 +#define TF_REUSE_SOCKET 0x02 +#define TF_WRITE_BEHIND 0x04 +#define TF_USE_DEFAULT_WORKER 0x00 +#define TF_USE_SYSTEM_THREAD 0x10 +#define TF_USE_KERNEL_APC 0x20 + +#include +#ifndef __MSWSOCK_WS1_SHARED + WINBOOL WINAPI TransmitFile(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + WINBOOL WINAPI AcceptEx(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + VOID WINAPI GetAcceptExSockaddrs(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); +#endif /* __MSWSOCK_WS1_SHARED */ + + typedef WINBOOL (WINAPI *LPFN_TRANSMITFILE)(SOCKET hSocket,HANDLE hFile,DWORD nNumberOfBytesToWrite,DWORD nNumberOfBytesPerSend,LPOVERLAPPED lpOverlapped,LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,DWORD dwReserved); + +#define WSAID_TRANSMITFILE {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef WINBOOL (WINAPI *LPFN_ACCEPTEX)(SOCKET sListenSocket,SOCKET sAcceptSocket,PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,LPDWORD lpdwBytesReceived,LPOVERLAPPED lpOverlapped); + +#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef VOID (WINAPI *LPFN_GETACCEPTEXSOCKADDRS)(PVOID lpOutputBuffer,DWORD dwReceiveDataLength,DWORD dwLocalAddressLength,DWORD dwRemoteAddressLength,struct sockaddr **LocalSockaddr,LPINT LocalSockaddrLength,struct sockaddr **RemoteSockaddr,LPINT RemoteSockaddrLength); + +#define WSAID_GETACCEPTEXSOCKADDRS {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + + typedef struct _TRANSMIT_PACKETS_ELEMENT { + ULONG dwElFlags; +#define TP_ELEMENT_MEMORY 1 +#define TP_ELEMENT_FILE 2 +#define TP_ELEMENT_EOP 4 + ULONG cLength; + __MINGW_EXTENSION union { + __MINGW_EXTENSION struct { + LARGE_INTEGER nFileOffset; + HANDLE hFile; + }; + PVOID pBuffer; + }; + } TRANSMIT_PACKETS_ELEMENT,*PTRANSMIT_PACKETS_ELEMENT,*LPTRANSMIT_PACKETS_ELEMENT; + +#define TP_DISCONNECT TF_DISCONNECT +#define TP_REUSE_SOCKET TF_REUSE_SOCKET +#define TP_USE_DEFAULT_WORKER TF_USE_DEFAULT_WORKER +#define TP_USE_SYSTEM_THREAD TF_USE_SYSTEM_THREAD +#define TP_USE_KERNEL_APC TF_USE_KERNEL_APC + + typedef WINBOOL (WINAPI *LPFN_TRANSMITPACKETS) (SOCKET hSocket,LPTRANSMIT_PACKETS_ELEMENT lpPacketArray,DWORD nElementCount,DWORD nSendSize,LPOVERLAPPED lpOverlapped,DWORD dwFlags); + +#define WSAID_TRANSMITPACKETS {0xd9689da0,0x1f90,0x11d3,{0x99,0x71,0x00,0xc0,0x4f,0x68,0xc8,0x76}} + + typedef WINBOOL (WINAPI *LPFN_CONNECTEX)(SOCKET s,const struct sockaddr *name,int namelen,PVOID lpSendBuffer,DWORD dwSendDataLength,LPDWORD lpdwBytesSent,LPOVERLAPPED lpOverlapped); + +#define WSAID_CONNECTEX {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} + + typedef WINBOOL (WINAPI *LPFN_DISCONNECTEX)(SOCKET s,LPOVERLAPPED lpOverlapped,DWORD dwFlags,DWORD dwReserved); + +#define WSAID_DISCONNECTEX {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} + +#define DE_REUSE_SOCKET TF_REUSE_SOCKET + +#define NLA_NAMESPACE_GUID {0x6642243a,0x3ba8,0x4aa6,{0xba,0xa5,0x2e,0xb,0xd7,0x1f,0xdd,0x83}} + +#define NLA_SERVICE_CLASS_GUID {0x37e515,0xb5c9,0x4a43,{0xba,0xda,0x8b,0x48,0xa8,0x7a,0xd2,0x39}} + +#define NLA_ALLUSERS_NETWORK 0x00000001 +#define NLA_FRIENDLY_NAME 0x00000002 + + typedef enum _NLA_BLOB_DATA_TYPE { + NLA_RAW_DATA = 0,NLA_INTERFACE = 1,NLA_802_1X_LOCATION = 2,NLA_CONNECTIVITY = 3,NLA_ICS = 4 + } NLA_BLOB_DATA_TYPE,*PNLA_BLOB_DATA_TYPE; + + typedef enum _NLA_CONNECTIVITY_TYPE { + NLA_NETWORK_AD_HOC = 0,NLA_NETWORK_MANAGED = 1,NLA_NETWORK_UNMANAGED = 2,NLA_NETWORK_UNKNOWN = 3 + } NLA_CONNECTIVITY_TYPE,*PNLA_CONNECTIVITY_TYPE; + + typedef enum _NLA_INTERNET { + NLA_INTERNET_UNKNOWN = 0,NLA_INTERNET_NO = 1,NLA_INTERNET_YES = 2 + } NLA_INTERNET,*PNLA_INTERNET; + + typedef struct _NLA_BLOB { + struct { + NLA_BLOB_DATA_TYPE type; + DWORD dwSize; + DWORD nextOffset; + } header; + union { + CHAR rawData[1]; + struct { + DWORD dwType; + DWORD dwSpeed; + CHAR adapterName[1]; + } interfaceData; + struct { + CHAR information[1]; + } locationData; + struct { + NLA_CONNECTIVITY_TYPE type; + NLA_INTERNET internet; + } connectivity; + struct { + struct { + DWORD speed; + DWORD type; + DWORD state; + WCHAR machineName[256]; + WCHAR sharedAdapterName[256]; + } remote; + } ICS; + } data; + } NLA_BLOB,*PNLA_BLOB,*LPNLA_BLOB; + +#ifdef BADVPN_SHIPPED_MSWSOCK_DECLARE_WSAMSG + typedef struct _WSAMSG { + LPSOCKADDR name; + INT namelen; + LPWSABUF lpBuffers; + DWORD dwBufferCount; + WSABUF Control; + DWORD dwFlags; + } WSAMSG,*PWSAMSG,*LPWSAMSG; +#endif + + typedef struct _WSACMSGHDR { + SIZE_T cmsg_len; + INT cmsg_level; + INT cmsg_type; + } WSACMSGHDR,*PWSACMSGHDR,*LPWSACMSGHDR; + +#define WSA_CMSGHDR_ALIGN(length) (((length) + TYPE_ALIGNMENT(WSACMSGHDR)-1) & (~(TYPE_ALIGNMENT(WSACMSGHDR)-1))) +#define WSA_CMSGDATA_ALIGN(length) (((length) + MAX_NATURAL_ALIGNMENT-1) & (~(MAX_NATURAL_ALIGNMENT-1))) +#define WSA_CMSG_FIRSTHDR(msg) (((msg)->Control.len >= sizeof(WSACMSGHDR)) ? (LPWSACMSGHDR)(msg)->Control.buf : (LPWSACMSGHDR)NULL) +#define WSA_CMSG_NXTHDR(msg,cmsg) ((!(cmsg)) ? WSA_CMSG_FIRSTHDR(msg) : ((((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len) + sizeof(WSACMSGHDR)) > (u_char *)((msg)->Control.buf) + (msg)->Control.len) ? (LPWSACMSGHDR)NULL : (LPWSACMSGHDR)((u_char *)(cmsg) + WSA_CMSGHDR_ALIGN((cmsg)->cmsg_len)))) +#define WSA_CMSG_DATA(cmsg) ((u_char *)(cmsg) + WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR))) +#define WSA_CMSG_SPACE(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR) + WSA_CMSGHDR_ALIGN(length))) +#define WSA_CMSG_LEN(length) (WSA_CMSGDATA_ALIGN(sizeof(WSACMSGHDR)) + length) + +#define MSG_TRUNC 0x0100 +#define MSG_CTRUNC 0x0200 +#define MSG_BCAST 0x0400 +#define MSG_MCAST 0x0800 + + typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg, + LPDWORD lpdwNumberOfBytesRecvd, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}} + +#if(_WIN32_WINNT >= 0x0600) + typedef struct { + LPWSAMSG lpMsg; + DWORD dwFlags; + LPDWORD lpNumberOfBytesSent; + LPWSAOVERLAPPED lpOverlapped; + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine; + } WSASENDMSG, *LPWSASENDMSG; + + typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags, + LPDWORD lpNumberOfBytesSent, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); + +#define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} + +#endif /* (_WIN32_WINNT >= 0x0600) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _MSWSOCK_ */ diff --git a/external/badvpn_dns/misc/nonblocking.h b/external/badvpn_dns/misc/nonblocking.h new file mode 100644 index 00000000..fe82584a --- /dev/null +++ b/external/badvpn_dns/misc/nonblocking.h @@ -0,0 +1,51 @@ +/** + * @file nonblocking.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for enabling non-blocking mode for a file descriptor. + */ + +#ifndef BADVPN_MISC_NONBLOCKING_H +#define BADVPN_MISC_NONBLOCKING_H + +#include +#include + +static int badvpn_set_nonblocking (int fd); + +int badvpn_set_nonblocking (int fd) +{ + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + return 0; + } + + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/nsskey.h b/external/badvpn_dns/misc/nsskey.h new file mode 100644 index 00000000..8268235f --- /dev/null +++ b/external/badvpn_dns/misc/nsskey.h @@ -0,0 +1,118 @@ +/** + * @file nsskey.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for opening a NSS certificate and its private key. + */ + +#ifndef BADVPN_MISC_NSSKEY_H +#define BADVPN_MISC_NSSKEY_H + +#include + +#include +#include +#include +#include + +#include + +#include + +/** + * Opens a NSS certificate and its private key. + * + * @param name name of the certificate + * @param out_cert on success, the certificate will be returned here. Should be + * released with CERT_DestroyCertificate. + * @param out_key on success, the private key will be returned here. Should be + * released with SECKEY_DestroyPrivateKey. + * @return 1 on success, 0 on failure + */ +static int open_nss_cert_and_key (char *name, CERTCertificate **out_cert, SECKEYPrivateKey **out_key) WARN_UNUSED; + +static SECKEYPrivateKey * find_nss_private_key (char *name) +{ + SECKEYPrivateKey *key = NULL; + + PK11SlotList *slot_list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL); + if (!slot_list) { + return NULL; + } + + PK11SlotListElement *slot_entry; + for (slot_entry = slot_list->head; !key && slot_entry; slot_entry = slot_entry->next) { + SECKEYPrivateKeyList *key_list = PK11_ListPrivKeysInSlot(slot_entry->slot, name, NULL); + if (!key_list) { + BLog(BLOG_ERROR, "PK11_ListPrivKeysInSlot failed"); + continue; + } + + SECKEYPrivateKeyListNode *key_node; + for (key_node = PRIVKEY_LIST_HEAD(key_list); !key && !PRIVKEY_LIST_END(key_node, key_list); key_node = PRIVKEY_LIST_NEXT(key_node)) { + char *key_name = PK11_GetPrivateKeyNickname(key_node->key); + if (!key_name || strcmp(key_name, name)) { + PORT_Free((void *)key_name); + continue; + } + PORT_Free((void *)key_name); + + key = SECKEY_CopyPrivateKey(key_node->key); + } + + SECKEY_DestroyPrivateKeyList(key_list); + } + + PK11_FreeSlotList(slot_list); + + return key; +} + +int open_nss_cert_and_key (char *name, CERTCertificate **out_cert, SECKEYPrivateKey **out_key) +{ + CERTCertificate *cert; + cert = CERT_FindCertByNicknameOrEmailAddr(CERT_GetDefaultCertDB(), name); + if (!cert) { + BLog(BLOG_ERROR, "CERT_FindCertByName failed (%d)", (int)PR_GetError()); + return 0; + } + + SECKEYPrivateKey *key = find_nss_private_key(name); + if (!key) { + BLog(BLOG_ERROR, "Failed to find private key"); + CERT_DestroyCertificate(cert); + return 0; + } + + *out_cert = cert; + *out_key = key; + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/offset.h b/external/badvpn_dns/misc/offset.h new file mode 100644 index 00000000..23b7683c --- /dev/null +++ b/external/badvpn_dns/misc/offset.h @@ -0,0 +1,51 @@ +/** + * @file offset.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Macros for determining offsets of members in structs. + */ + +#ifndef BADVPN_MISC_OFFSET_H +#define BADVPN_MISC_OFFSET_H + +#include +#include + +/** + * Returns a pointer to a struct, given a pointer to its member. + */ +#define UPPER_OBJECT(_ptr, _object_type, _field_name) ((_object_type *)((char *)(_ptr) - offsetof(_object_type, _field_name))) + +/** + * Returns the offset of one struct member from another. + * Expands to an int. + */ +#define OFFSET_DIFF(_object_type, _field1, _field2) ((int)offsetof(_object_type, _field1) - (int)offsetof(_object_type, _field2)) + +#endif diff --git a/external/badvpn_dns/misc/open_standard_streams.h b/external/badvpn_dns/misc/open_standard_streams.h new file mode 100644 index 00000000..7fd6d414 --- /dev/null +++ b/external/badvpn_dns/misc/open_standard_streams.h @@ -0,0 +1,54 @@ +/** + * @file open_standard_streams.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_OPEN_STANDARD_STREAMS_H +#define BADVPN_OPEN_STANDARD_STREAMS_H + +#ifndef BADVPN_USE_WINAPI +#include +#include +#include +#include +#endif + +static void open_standard_streams (void) +{ +#ifndef BADVPN_USE_WINAPI + int fd; + + do { + fd = open("/dev/null", O_RDWR); + if (fd > 2) { + close(fd); + } + } while (fd >= 0 && fd <= 2); +#endif +} + +#endif diff --git a/external/badvpn_dns/misc/overflow.h b/external/badvpn_dns/misc/overflow.h new file mode 100644 index 00000000..9fde52ad --- /dev/null +++ b/external/badvpn_dns/misc/overflow.h @@ -0,0 +1,66 @@ +/** + * @file overflow.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Functions for checking for overflow of integer addition. + */ + +#ifndef BADVPN_MISC_OVERFLOW_H +#define BADVPN_MISC_OVERFLOW_H + +#include +#include + +#define DEFINE_UNSIGNED_OVERFLOW(_name, _type, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + return (b > _max - a); \ +} + +#define DEFINE_SIGNED_OVERFLOW(_name, _type, _min, _max) \ +static int add_ ## _name ## _overflows (_type a, _type b) \ +{\ + if ((a < 0) ^ (b < 0)) return 0; \ + if (a < 0) return -(a < _min - b); \ + return (a > _max - b); \ +} + +DEFINE_UNSIGNED_OVERFLOW(uint, unsigned int, UINT_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint8, uint8_t, UINT8_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint16, uint16_t, UINT16_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint32, uint32_t, UINT32_MAX) +DEFINE_UNSIGNED_OVERFLOW(uint64, uint64_t, UINT64_MAX) + +DEFINE_SIGNED_OVERFLOW(int, int, INT_MIN, INT_MAX) +DEFINE_SIGNED_OVERFLOW(int8, int8_t, INT8_MIN, INT8_MAX) +DEFINE_SIGNED_OVERFLOW(int16, int16_t, INT16_MIN, INT16_MAX) +DEFINE_SIGNED_OVERFLOW(int32, int32_t, INT32_MIN, INT32_MAX) +DEFINE_SIGNED_OVERFLOW(int64, int64_t, INT64_MIN, INT64_MAX) + +#endif diff --git a/external/badvpn_dns/misc/packed.h b/external/badvpn_dns/misc/packed.h new file mode 100644 index 00000000..2175536a --- /dev/null +++ b/external/badvpn_dns/misc/packed.h @@ -0,0 +1,51 @@ +/** + * @file packed.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Structure packing macros. + */ + +#ifndef BADVPN_PACKED_H +#define BADVPN_PACKED_H + +#ifdef _MSC_VER + +#define B_START_PACKED __pragma(pack(push, 1)) +#define B_END_PACKED __pragma(pack(pop)) +#define B_PACKED + +#else + +#define B_START_PACKED +#define B_END_PACKED +#define B_PACKED __attribute__((packed)) + +#endif + +#endif diff --git a/external/badvpn_dns/misc/parse_number.h b/external/badvpn_dns/misc/parse_number.h new file mode 100644 index 00000000..2601ebbf --- /dev/null +++ b/external/badvpn_dns/misc/parse_number.h @@ -0,0 +1,304 @@ +/** + * @file parse_number.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Numeric string parsing. + */ + +#ifndef BADVPN_MISC_PARSE_NUMBER_H +#define BADVPN_MISC_PARSE_NUMBER_H + +#include +#include +#include +#include + +#include +#include + +// public parsing functions +static int decode_decimal_digit (char c); +static int decode_hex_digit (char c); +static int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) WARN_UNUSED; +static int parse_unsigned_hex_integer (const char *str, uintmax_t *out) WARN_UNUSED; +static int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; +static int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) WARN_UNUSED; + +// public generation functions +static int compute_decimal_repr_size (uintmax_t x); +static void generate_decimal_repr (uintmax_t x, char *out, int repr_size); +static int generate_decimal_repr_string (uintmax_t x, char *out); + +// implementation follows + +// decimal representation of UINTMAX_MAX +static const char parse_number__uintmax_max_str[] = "18446744073709551615"; + +// make sure UINTMAX_MAX is what we think it is +static const char parse_number__uintmax_max_str_assert[(UINTMAX_MAX == UINTMAX_C(18446744073709551615)) ? 1 : -1]; + +static int decode_decimal_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } + + return -1; +} + +static int decode_hex_digit (char c) +{ + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': case 'a': return 10; + case 'B': case 'b': return 11; + case 'C': case 'c': return 12; + case 'D': case 'd': return 13; + case 'E': case 'e': return 14; + case 'F': case 'f': return 15; + } + + return -1; +} + +static int parse__no_overflow (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + while (str_len > 0) { + if (*str < '0' || *str > '9') { + return 0; + } + + n = 10 * n + (*str - '0'); + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + // we do not allow empty strings + if (str_len == 0) { + return 0; + } + + // remove leading zeros + while (str_len > 0 && *str == '0') { + str++; + str_len--; + } + + // detect overflow + if (str_len > sizeof(parse_number__uintmax_max_str) - 1 || + (str_len == sizeof(parse_number__uintmax_max_str) - 1 && memcmp(str, parse_number__uintmax_max_str, sizeof(parse_number__uintmax_max_str) - 1) > 0)) { + return 0; + } + + // will not overflow (but can still have invalid characters) + return parse__no_overflow(str, str_len, out); +} + +int parse_unsigned_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_integer_bin(str, strlen(str), out); +} + +int parse_unsigned_integer_cstr (b_cstring cstr, size_t offset, size_t length, uintmax_t *out) +{ + b_cstring_assert_range(cstr, offset, length); + + if (length == 0) { + return 0; + } + + uintmax_t n = 0; + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + for (size_t i = 0; i < chunk_length; i++) { + int digit = decode_decimal_digit(chunk_data[i]); + if (digit < 0) { + return 0; + } + if (n > UINTMAX_MAX / 10) { + return 0; + } + n *= 10; + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + } + }) + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer_bin (const char *str, size_t str_len, uintmax_t *out) +{ + uintmax_t n = 0; + + if (str_len == 0) { + return 0; + } + + while (str_len > 0) { + int digit = decode_hex_digit(*str); + if (digit < 0) { + return 0; + } + + if (n > UINTMAX_MAX / 16) { + return 0; + } + n *= 16; + + if (digit > UINTMAX_MAX - n) { + return 0; + } + n += digit; + + str++; + str_len--; + } + + *out = n; + return 1; +} + +int parse_unsigned_hex_integer (const char *str, uintmax_t *out) +{ + return parse_unsigned_hex_integer_bin(str, strlen(str), out); +} + +int parse_signmag_integer_bin (const char *str, size_t str_len, int *out_sign, uintmax_t *out_mag) +{ + int sign = 1; + if (str_len > 0 && (str[0] == '+' || str[0] == '-')) { + sign = 1 - 2 * (str[0] == '-'); + str++; + str_len--; + } + + if (!parse_unsigned_integer_bin(str, str_len, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int parse_signmag_integer (const char *str, int *out_sign, uintmax_t *out_mag) +{ + return parse_signmag_integer_bin(str, strlen(str), out_sign, out_mag); +} + +int parse_signmag_integer_cstr (b_cstring cstr, size_t offset, size_t length, int *out_sign, uintmax_t *out_mag) +{ + b_cstring_assert_range(cstr, offset, length); + + int sign = 1; + if (length > 0 && (b_cstring_at(cstr, offset) == '+' || b_cstring_at(cstr, offset) == '-')) { + sign = 1 - 2 * (b_cstring_at(cstr, offset) == '-'); + offset++; + length--; + } + + if (!parse_unsigned_integer_cstr(cstr, offset, length, out_mag)) { + return 0; + } + + *out_sign = sign; + return 1; +} + +int compute_decimal_repr_size (uintmax_t x) +{ + int size = 0; + + do { + size++; + x /= 10; + } while (x > 0); + + return size; +} + +void generate_decimal_repr (uintmax_t x, char *out, int repr_size) +{ + ASSERT(out) + ASSERT(repr_size == compute_decimal_repr_size(x)) + + out += repr_size; + + do { + *(--out) = '0' + (x % 10); + x /= 10; + } while (x > 0); +} + +int generate_decimal_repr_string (uintmax_t x, char *out) +{ + ASSERT(out) + + int repr_size = compute_decimal_repr_size(x); + generate_decimal_repr(x, out, repr_size); + out[repr_size] = '\0'; + + return repr_size; +} + +#endif diff --git a/external/badvpn_dns/misc/print_macros.h b/external/badvpn_dns/misc/print_macros.h new file mode 100644 index 00000000..a07fdbe4 --- /dev/null +++ b/external/badvpn_dns/misc/print_macros.h @@ -0,0 +1,98 @@ +/** + * @file print_macros.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Format macros for printf() for non-standard compilers. + */ + +#ifndef BADVPN_PRINT_MACROS +#define BADVPN_PRINT_MACROS + +#ifdef _MSC_VER + +// size_t +#define PRIsz "Iu" + +// signed exact width (intN_t) +#define PRId8 "d" +#define PRIi8 "i" +#define PRId16 "d" +#define PRIi16 "i" +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRId64 "I64d" +#define PRIi64 "I64i" + +// unsigned exact width (uintN_t) +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIo16 "o" +#define PRIu16 "u" +#define PRIx16 "x" +#define PRIX16 "X" +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" + +// signed maximum width (intmax_t) +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +// unsigned maximum width (uintmax_t) +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +// signed pointer (intptr_t) +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// unsigned pointer (uintptr_t) +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +#else + +#include + +#define PRIsz "zu" + +#endif + +#endif diff --git a/external/badvpn_dns/misc/read_file.h b/external/badvpn_dns/misc/read_file.h new file mode 100644 index 00000000..e1862e5b --- /dev/null +++ b/external/badvpn_dns/misc/read_file.h @@ -0,0 +1,98 @@ +/** + * @file read_file.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for reading a file into memory using stdio. + */ + +#ifndef BADVPN_MISC_READ_FILE_H +#define BADVPN_MISC_READ_FILE_H + +#include +#include +#include +#include + +static int read_file (const char *file, uint8_t **out_data, size_t *out_len) +{ + FILE *f = fopen(file, "r"); + if (!f) { + goto fail0; + } + + size_t buf_len = 0; + size_t buf_size = 128; + + uint8_t *buf = (uint8_t *)malloc(buf_size); + if (!buf) { + goto fail1; + } + + while (1) { + if (buf_len == buf_size) { + if (2 > SIZE_MAX / buf_size) { + goto fail; + } + size_t newsize = 2 * buf_size; + + uint8_t *newbuf = (uint8_t *)realloc(buf, newsize); + if (!newbuf) { + goto fail; + } + + buf = newbuf; + buf_size = newsize; + } + + size_t bytes = fread(buf + buf_len, 1, buf_size - buf_len, f); + if (bytes == 0) { + if (feof(f)) { + break; + } + goto fail; + } + + buf_len += bytes; + } + + fclose(f); + + *out_data = buf; + *out_len = buf_len; + return 1; + +fail: + free(buf); +fail1: + fclose(f); +fail0: + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/read_write_int.h b/external/badvpn_dns/misc/read_write_int.h new file mode 100644 index 00000000..bc4ed2c0 --- /dev/null +++ b/external/badvpn_dns/misc/read_write_int.h @@ -0,0 +1,181 @@ +/** + * @file read_write_int.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_READ_WRITE_INT_H +#define BADVPN_READ_WRITE_INT_H + +#include + +static uint8_t badvpn_read_le8 (const char *c_ptr); +static uint16_t badvpn_read_le16 (const char *c_ptr); +static uint32_t badvpn_read_le32 (const char *c_ptr); +static uint64_t badvpn_read_le64 (const char *c_ptr); + +static uint8_t badvpn_read_be8 (const char *c_ptr); +static uint16_t badvpn_read_be16 (const char *c_ptr); +static uint32_t badvpn_read_be32 (const char *c_ptr); +static uint64_t badvpn_read_be64 (const char *c_ptr); + +static void badvpn_write_le8 (uint8_t x, char *c_ptr); +static void badvpn_write_le16 (uint16_t x, char *c_ptr); +static void badvpn_write_le32 (uint32_t x, char *c_ptr); +static void badvpn_write_le64 (uint64_t x, char *c_ptr); + +static void badvpn_write_be8 (uint8_t x, char *c_ptr); +static void badvpn_write_be16 (uint16_t x, char *c_ptr); +static void badvpn_write_be32 (uint32_t x, char *c_ptr); +static void badvpn_write_be64 (uint64_t x, char *c_ptr); + +static uint8_t badvpn_read_le8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_le16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[1] << 8) | ((uint16_t)ptr[0] << 0); +} + +static uint32_t badvpn_read_le32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[3] << 24) | ((uint32_t)ptr[2] << 16) | + ((uint32_t)ptr[1] << 8) | ((uint32_t)ptr[0] << 0); +} + +static uint64_t badvpn_read_le64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[7] << 56) | ((uint64_t)ptr[6] << 48) | + ((uint64_t)ptr[5] << 40) | ((uint64_t)ptr[4] << 32) | + ((uint64_t)ptr[3] << 24) | ((uint64_t)ptr[2] << 16) | + ((uint64_t)ptr[1] << 8) | ((uint64_t)ptr[0] << 0); +} + +static uint8_t badvpn_read_be8 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint8_t)ptr[0] << 0); +} + +static uint16_t badvpn_read_be16 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint16_t)ptr[0] << 8) | ((uint16_t)ptr[1] << 0); +} + +static uint32_t badvpn_read_be32 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | + ((uint32_t)ptr[2] << 8) | ((uint32_t)ptr[3] << 0); +} + +static uint64_t badvpn_read_be64 (const char *c_ptr) +{ + const uint8_t *ptr = (const uint8_t *)c_ptr; + return ((uint64_t)ptr[0] << 56) | ((uint64_t)ptr[1] << 48) | + ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[3] << 32) | + ((uint64_t)ptr[4] << 24) | ((uint64_t)ptr[5] << 16) | + ((uint64_t)ptr[6] << 8) | ((uint64_t)ptr[7] << 0); +} + +static void badvpn_write_le8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_le16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_le64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[7] = x >> 56; + ptr[6] = x >> 48; + ptr[5] = x >> 40; + ptr[4] = x >> 32; + ptr[3] = x >> 24; + ptr[2] = x >> 16; + ptr[1] = x >> 8; + ptr[0] = x >> 0; +} + +static void badvpn_write_be8 (uint8_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 0; +} + +static void badvpn_write_be16 (uint16_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 8; + ptr[1] = x >> 0; +} + +static void badvpn_write_be32 (uint32_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 24; + ptr[1] = x >> 16; + ptr[2] = x >> 8; + ptr[3] = x >> 0; +} + +static void badvpn_write_be64 (uint64_t x, char *c_ptr) +{ + uint8_t *ptr = (uint8_t *)c_ptr; + ptr[0] = x >> 56; + ptr[1] = x >> 48; + ptr[2] = x >> 40; + ptr[3] = x >> 32; + ptr[4] = x >> 24; + ptr[5] = x >> 16; + ptr[6] = x >> 8; + ptr[7] = x >> 0; +} + +#endif diff --git a/external/badvpn_dns/misc/socks_proto.h b/external/badvpn_dns/misc/socks_proto.h new file mode 100644 index 00000000..41f5a1fd --- /dev/null +++ b/external/badvpn_dns/misc/socks_proto.h @@ -0,0 +1,118 @@ +/** + * @file socks_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the SOCKS protocol. + */ + +#ifndef BADVPN_MISC_SOCKS_PROTO_H +#define BADVPN_MISC_SOCKS_PROTO_H + +#include + +#include + +#define SOCKS_VERSION 0x05 + +#define SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED 0x00 +#define SOCKS_METHOD_GSSAPI 0x01 +#define SOCKS_METHOD_USERNAME_PASSWORD 0x02 +#define SOCKS_METHOD_NO_ACCEPTABLE_METHODS 0xFF + +#define SOCKS_CMD_CONNECT 0x01 +#define SOCKS_CMD_BIND 0x02 +#define SOCKS_CMD_UDP_ASSOCIATE 0x03 + +#define SOCKS_ATYP_IPV4 0x01 +#define SOCKS_ATYP_DOMAINNAME 0x03 +#define SOCKS_ATYP_IPV6 0x04 + +#define SOCKS_REP_SUCCEEDED 0x00 +#define SOCKS_REP_GENERAL_FAILURE 0x01 +#define SOCKS_REP_CONNECTION_NOT_ALLOWED 0x02 +#define SOCKS_REP_NETWORK_UNREACHABLE 0x03 +#define SOCKS_REP_HOST_UNREACHABLE 0x04 +#define SOCKS_REP_CONNECTION_REFUSED 0x05 +#define SOCKS_REP_TTL_EXPIRED 0x06 +#define SOCKS_REP_COMMAND_NOT_SUPPORTED 0x07 +#define SOCKS_REP_ADDRESS_TYPE_NOT_SUPPORTED 0x08 + +B_START_PACKED +struct socks_client_hello_header { + uint8_t ver; + uint8_t nmethods; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_client_hello_method { + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_server_hello { + uint8_t ver; + uint8_t method; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_request_header { + uint8_t ver; + uint8_t cmd; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_reply_header { + uint8_t ver; + uint8_t rep; + uint8_t rsv; + uint8_t atyp; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv4 { + uint32_t addr; + uint16_t port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_addr_ipv6 { + uint8_t addr[16]; + uint16_t port; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/misc/sslsocket.h b/external/badvpn_dns/misc/sslsocket.h new file mode 100644 index 00000000..71e43c20 --- /dev/null +++ b/external/badvpn_dns/misc/sslsocket.h @@ -0,0 +1,48 @@ +/** + * @file sslsocket.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Structure for moving around sockets, possibly together with SSL compoments. + */ + +#ifndef BADVPN_MISC_SSLSOCKET_H +#define BADVPN_MISC_SSLSOCKET_H + +#include + +#include +#include + +typedef struct { + BConnection con; + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; +} sslsocket; + +#endif diff --git a/external/badvpn_dns/misc/stdbuf_cmdline.h b/external/badvpn_dns/misc/stdbuf_cmdline.h new file mode 100644 index 00000000..8459c645 --- /dev/null +++ b/external/badvpn_dns/misc/stdbuf_cmdline.h @@ -0,0 +1,92 @@ +/** + * @file stdbuf_cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Builds command line for running a program via stdbuf. + */ + +#ifndef BADVPN_STDBUF_CMDLINE_H +#define BADVPN_STDBUF_CMDLINE_H + +#include + +#include +#include +#include + +/** + * Builds the initial part of command line for calling a program via stdbuf + * with standard output buffering set to line-buffered. + * + * @param out {@link CmdLine} to append the result to. Note than on failure, only + * some part of the cmdline may have been appended. + * @param stdbuf_exec full path to stdbuf executable + * @param exec path to the executable. Must not contain nulls. + * @param exec_len number of characters in exec + * @return 1 on success, 0 on failure + */ +static int build_stdbuf_cmdline (CmdLine *out, const char *stdbuf_exec, const char *exec, size_t exec_len) WARN_UNUSED; + +int build_stdbuf_cmdline (CmdLine *out, const char *stdbuf_exec, const char *exec, size_t exec_len) +{ + ASSERT(!memchr(exec, '\0', exec_len)) + + if (!CmdLine_AppendMulti(out, 3, stdbuf_exec, "-o", "L")) { + goto fail1; + } + + if (exec[0] == '/') { + if (!CmdLine_AppendNoNull(out, exec, exec_len)) { + goto fail1; + } + } else { + bsize_t size = bsize_add(bsize_fromsize(exec_len), bsize_fromsize(3)); + char *real_exec = BAllocSize(size); + if (!real_exec) { + goto fail1; + } + + memcpy(real_exec, "./", 2); + memcpy(real_exec + 2, exec, exec_len); + real_exec[2 + exec_len] = '\0'; + + int res = CmdLine_Append(out, real_exec); + free(real_exec); + if (!res) { + goto fail1; + } + } + + return 1; + +fail1: + return 0; +} + +#endif diff --git a/external/badvpn_dns/misc/strdup.h b/external/badvpn_dns/misc/strdup.h new file mode 100644 index 00000000..2e475bb7 --- /dev/null +++ b/external/badvpn_dns/misc/strdup.h @@ -0,0 +1,86 @@ +/** + * @file strdup.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Allocate memory for a string and copy it there. + */ + +#ifndef BADVPN_STRDUP_H +#define BADVPN_STRDUP_H + +#include +#include +#include + +#include + +/** + * Allocate and copy a null-terminated string. + */ +static char * b_strdup (const char *str) +{ + ASSERT(str) + + size_t len = strlen(str); + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len + 1); + + return s; +} + +/** + * Allocate memory for a null-terminated string and use the + * given data as its contents. A null terminator is appended + * after the specified data. + */ +static char * b_strdup_bin (const char *str, size_t len) +{ + ASSERT(str) + + if (len == SIZE_MAX) { + return NULL; + } + + char *s = (char *)malloc(len + 1); + if (!s) { + return NULL; + } + + memcpy(s, str, len); + s[len] = '\0'; + + return s; +} + +#endif diff --git a/external/badvpn_dns/misc/string_begins_with.h b/external/badvpn_dns/misc/string_begins_with.h new file mode 100644 index 00000000..3c9c5e91 --- /dev/null +++ b/external/badvpn_dns/misc/string_begins_with.h @@ -0,0 +1,96 @@ +/** + * @file string_begins_with.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Function for checking if a string begins with a given string. + */ + +#ifndef BADVPN_MISC_STRING_BEGINS_WITH +#define BADVPN_MISC_STRING_BEGINS_WITH + +#include +#include + +#include + +static size_t data_begins_with (const char *str, size_t str_len, const char *needle) +{ + ASSERT(strlen(needle) > 0) + + size_t len = 0; + + while (str_len > 0 && *needle) { + if (*str != *needle) { + return 0; + } + str++; + str_len--; + needle++; + len++; + } + + if (*needle) { + return 0; + } + + return len; +} + +static size_t string_begins_with (const char *str, const char *needle) +{ + ASSERT(strlen(needle) > 0) + + return data_begins_with(str, strlen(str), needle); +} + +static size_t data_begins_with_bin (const char *str, size_t str_len, const char *needle, size_t needle_len) +{ + ASSERT(needle_len > 0) + + size_t len = 0; + + while (str_len > 0 && needle_len > 0) { + if (*str != *needle) { + return 0; + } + str++; + str_len--; + needle++; + needle_len--; + len++; + } + + if (needle_len > 0) { + return 0; + } + + return len; +} + +#endif diff --git a/external/badvpn_dns/misc/substring.h b/external/badvpn_dns/misc/substring.h new file mode 100644 index 00000000..b3a8fff1 --- /dev/null +++ b/external/badvpn_dns/misc/substring.h @@ -0,0 +1,81 @@ +#include + +#include + +static void build_substring_backtrack_table (const char *str, size_t len, size_t *out_table) +{ + ASSERT(len > 0) + + size_t x = 0; + + for (size_t i = 1; i < len; i++) { + out_table[i] = x; + while (x > 0 && str[i] != str[x]) { + x = out_table[x]; + } + if (str[i] == str[x]) { + x++; + } + } +} + +static int find_substring (const char *text, size_t text_len, const char *word, size_t word_len, const size_t *table, size_t *out_position) +{ + ASSERT(word_len > 0) + + size_t x = 0; + + for (size_t i = 0; i < text_len; i++) { + while (x > 0 && text[i] != word[x]) { + x = table[x]; + } + if (text[i] == word[x]) { + if (x + 1 == word_len) { + *out_position = i - x; + return 1; + } + x++; + } + } + + return 0; +} + +static void build_substring_backtrack_table_reverse (const char *str, size_t len, size_t *out_table) +{ + ASSERT(len > 0) + + size_t x = 0; + + for (size_t i = 1; i < len; i++) { + out_table[i] = x; + while (x > 0 && str[len - 1 - i] != str[len - 1 - x]) { + x = out_table[x]; + } + if (str[len - 1 - i] == str[len - 1 - x]) { + x++; + } + } +} + +static int find_substring_reverse (const char *text, size_t text_len, const char *word, size_t word_len, const size_t *table, size_t *out_position) +{ + ASSERT(word_len > 0) + + size_t x = 0; + + for (size_t i = 0; i < text_len; i++) { + while (x > 0 && text[text_len - 1 - i] != word[word_len - 1 - x]) { + x = table[x]; + } + if (text[text_len - 1 - i] == word[word_len - 1 - x]) { + if (x + 1 == word_len) { + *out_position = (text_len - 1 - i); + return 1; + } + x++; + } + } + + return 0; +} diff --git a/external/badvpn_dns/misc/udp_proto.h b/external/badvpn_dns/misc/udp_proto.h new file mode 100644 index 00000000..23ec69ae --- /dev/null +++ b/external/badvpn_dns/misc/udp_proto.h @@ -0,0 +1,170 @@ +/** + * @file udp_proto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for the UDP protocol. + */ + +#ifndef BADVPN_MISC_UDP_PROTO_H +#define BADVPN_MISC_UDP_PROTO_H + +#include + +#include +#include +#include +#include +#include + +B_START_PACKED +struct udp_header { + uint16_t source_port; + uint16_t dest_port; + uint16_t length; + uint16_t checksum; +} B_PACKED; +B_END_PACKED + +static uint32_t udp_checksum_summer (const char *data, uint16_t len) +{ + ASSERT(len % 2 == 0) + + uint32_t t = 0; + + for (uint16_t i = 0; i < len / 2; i++) { + t += badvpn_read_be16(data + 2 * i); + } + + return t; +} + +static uint16_t udp_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, uint32_t source_addr, uint32_t dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((char *)&source_addr, sizeof(source_addr)); + t += udp_checksum_summer((char *)&dest_addr, sizeof(dest_addr)); + + uint16_t x; + x = hton16(IPV4_PROTOCOL_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton16(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + x = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&x, sizeof(x)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static uint16_t udp_ip6_checksum (const struct udp_header *header, const uint8_t *payload, uint16_t payload_len, const uint8_t *source_addr, const uint8_t *dest_addr) +{ + uint32_t t = 0; + + t += udp_checksum_summer((const char *)source_addr, 16); + t += udp_checksum_summer((const char *)dest_addr, 16); + + uint32_t x; + x = hton32(sizeof(*header) + payload_len); + t += udp_checksum_summer((char *)&x, sizeof(x)); + x = hton32(IPV6_NEXT_UDP); + t += udp_checksum_summer((char *)&x, sizeof(x)); + + t += udp_checksum_summer((const char *)header, sizeof(*header)); + + if (payload_len % 2 == 0) { + t += udp_checksum_summer((const char *)payload, payload_len); + } else { + t += udp_checksum_summer((const char *)payload, payload_len - 1); + + uint16_t y; + y = hton16(((uint16_t)payload[payload_len - 1]) << 8); + t += udp_checksum_summer((char *)&y, sizeof(y)); + } + + while (t >> 16) { + t = (t & 0xFFFF) + (t >> 16); + } + + if (t == 0) { + t = UINT16_MAX; + } + + return hton16(~t); +} + +static int udp_check (const uint8_t *data, int data_len, struct udp_header *out_header, uint8_t **out_payload, int *out_payload_len) +{ + ASSERT(data_len >= 0) + ASSERT(out_header) + ASSERT(out_payload) + ASSERT(out_payload_len) + + // parse UDP header + if (data_len < sizeof(struct udp_header)) { + return 0; + } + memcpy(out_header, data, sizeof(*out_header)); + data += sizeof(*out_header); + data_len -= sizeof(*out_header); + + // verify UDP payload + int udp_length = ntoh16(out_header->length); + if (udp_length < sizeof(*out_header)) { + return 0; + } + if (udp_length > sizeof(*out_header) + data_len) { + return 0; + } + + // ignore stray data + data_len = udp_length - sizeof(*out_header); + + *out_payload = (uint8_t *)data; + *out_payload_len = data_len; + return 1; +} + +#endif diff --git a/external/badvpn_dns/misc/unicode_funcs.h b/external/badvpn_dns/misc/unicode_funcs.h new file mode 100644 index 00000000..2442e7fe --- /dev/null +++ b/external/badvpn_dns/misc/unicode_funcs.h @@ -0,0 +1,232 @@ +/** + * @file unicode_funcs.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UNICODE_FUNCS_H +#define BADVPN_UNICODE_FUNCS_H + +#include +#include +#include +#include +#include +#include + +/** + * Decodes UTF-16 data as bytes into an allocated null-terminated UTF-8 string. + * + * @param data UTF-16 data, in big endian + * @param data_len size of data in bytes + * @param out_is_error if not NULL and the function returns a string, + * *out_is_error will be set to 0 or 1, indicating + * whether there have been errors decoding the input. + * A null decoded character is treated as an error. + * @return An UTF-8 null-terminated string which can be freed with free(), + * or NULL if out of memory. + */ +static char * unicode_decode_utf16_to_utf8 (const uint8_t *data, size_t data_len, int *out_is_error); + +/** + * Decodes UTF-8 data into UTF-16 data as bytes. + * + * @param data UTF-8 data + * @param data_len size of data in bytes + * @param out output buffer + * @param out_avail number of bytes available in output buffer + * @param out_len if not NULL, *out_len will contain the number of bytes + * required to store the resulting data (or overflow) + * @param out_is_error if not NULL, *out_is_error will contain 0 or 1, + * indicating whether there have been errors decoding + * the input + */ +static void unicode_decode_utf8_to_utf16 (const uint8_t *data, size_t data_len, uint8_t *out, size_t out_avail, bsize_t *out_len, int *out_is_error); + +static char * unicode_decode_utf16_to_utf8 (const uint8_t *data, size_t data_len, int *out_is_error) +{ + // will build the resulting UTF-8 string by appending to ExpString + ExpString str; + if (!ExpString_Init(&str)) { + goto fail0; + } + + // init UTF-16 decoder + Utf16Decoder decoder; + Utf16Decoder_Init(&decoder); + + // set initial input and input matching positions + size_t i_in = 0; + size_t i_ch = 0; + + int error = 0; + + while (i_in < data_len) { + // read two input bytes from the input position + uint8_t x = data[i_in++]; + if (i_in == data_len) { + break; + } + uint8_t y = data[i_in++]; + + // combine them into a 16-bit value + uint16_t xy = (((uint16_t)x << 8) | (uint16_t)y); + + // give the 16-bit value to the UTF-16 decoder and maybe + // receive a Unicode character back + uint32_t ch; + if (!Utf16Decoder_Input(&decoder, xy, &ch)) { + continue; + } + + if (!error) { + // encode the Unicode character back into UTF-16 + uint16_t chenc[2]; + int chenc_n = Utf16Encoder_EncodeCharacter(ch, chenc); + ASSERT(chenc_n > 0) + + // match the result with input + for (int chenc_i = 0; chenc_i < chenc_n; chenc_i++) { + uint8_t cx = (chenc[chenc_i] >> 8); + uint8_t cy = (chenc[chenc_i] & 0xFF); + + if (i_ch >= data_len || data[i_ch] != cx) { + error = 1; + break; + } + i_ch++; + + if (i_ch >= data_len || data[i_ch] != cy) { + error = 1; + break; + } + i_ch++; + } + } + + // we don't like null Unicode characters because we're building a + // null-terminated UTF-8 string + if (ch == 0) { + error = 1; + continue; + } + + // encode the Unicode character into UTF-8 + uint8_t enc[5]; + int enc_n = Utf8Encoder_EncodeCharacter(ch, enc); + ASSERT(enc_n > 0) + + // append the resulting UTF-8 bytes to the result string + enc[enc_n] = 0; + if (!ExpString_Append(&str, enc)) { + goto fail1; + } + } + + // check if we matched the whole input string when encoding back + if (i_ch < data_len) { + error = 1; + } + + if (out_is_error) { + *out_is_error = error; + } + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +static void unicode_decode_utf8_to_utf16 (const uint8_t *data, size_t data_len, uint8_t *out, size_t out_avail, bsize_t *out_len, int *out_is_error) +{ + Utf8Decoder decoder; + Utf8Decoder_Init(&decoder); + + size_t i_in = 0; + size_t i_ch = 0; + + bsize_t len = bsize_fromsize(0); + + int error = 0; + + while (i_in < data_len) { + uint8_t x = data[i_in++]; + + uint32_t ch; + if (!Utf8Decoder_Input(&decoder, x, &ch)) { + continue; + } + + if (!error) { + uint8_t chenc[4]; + int chenc_n = Utf8Encoder_EncodeCharacter(ch, chenc); + ASSERT(chenc_n > 0) + + for (int chenc_i = 0; chenc_i < chenc_n; chenc_i++) { + if (i_ch >= data_len || data[i_ch] != chenc[chenc_i]) { + error = 1; + break; + } + i_ch++; + } + } + + uint16_t enc[2]; + int enc_n = Utf16Encoder_EncodeCharacter(ch, enc); + ASSERT(enc_n > 0) + + len = bsize_add(len, bsize_fromsize(2 * enc_n)); + + for (int enc_i = 0; enc_i < enc_n; enc_i++) { + if (out_avail == 0) { + break; + } + *(out++) = (enc[enc_i] >> 8); + out_avail--; + + if (out_avail == 0) { + break; + } + *(out++) = (enc[enc_i] & 0xFF); + out_avail--; + } + } + + if (i_ch < data_len) { + error = 1; + } + + if (out_len) { + *out_len = len; + } + if (out_is_error) { + *out_is_error = error; + } +} + +#endif diff --git a/external/badvpn_dns/misc/version.h b/external/badvpn_dns/misc/version.h new file mode 100644 index 00000000..a90523f0 --- /dev/null +++ b/external/badvpn_dns/misc/version.h @@ -0,0 +1,41 @@ +/** + * @file version.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Product information definitions. + */ + +#ifndef BADVPN_MISC_VERSION_H +#define BADVPN_MISC_VERSION_H + +#define GLOBAL_PRODUCT_NAME "BadVPN" +#define GLOBAL_VERSION "1.999.129" +#define GLOBAL_COPYRIGHT_NOTICE "Copyright (C) 2010 Ambroz Bizjak " + +#endif diff --git a/external/badvpn_dns/misc/write_file.h b/external/badvpn_dns/misc/write_file.h new file mode 100644 index 00000000..97b1c197 --- /dev/null +++ b/external/badvpn_dns/misc/write_file.h @@ -0,0 +1,104 @@ +/** + * @file write_file.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_WRITE_FILE_H +#define BADVPN_WRITE_FILE_H + +#include +#include +#include + +#include +#include + +static int write_file (const char *file, const uint8_t *data, size_t len) +{ + FILE *f = fopen(file, "w"); + if (!f) { + goto fail0; + } + + while (len > 0) { + size_t res = fwrite(data, 1, len, f); + if (res == 0) { + goto fail1; + } + + ASSERT(res <= len) + + data += res; + len -= res; + } + + if (fclose(f) != 0) { + return 0; + } + + return 1; + +fail1: + fclose(f); +fail0: + return 0; +} + +static int write_file_cstring (const char *file, b_cstring cstr, size_t offset, size_t length) +{ + b_cstring_assert_range(cstr, offset, length); + + FILE *f = fopen(file, "w"); + if (!f) { + goto fail0; + } + + B_CSTRING_LOOP_RANGE(cstr, offset, length, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + size_t res = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, f); + if (res == 0) { + goto fail1; + } + ASSERT(res <= chunk_length - chunk_pos) + chunk_pos += res; + } + }) + + if (fclose(f) != 0) { + return 0; + } + + return 1; + +fail1: + fclose(f); +fail0: + return 0; +} + +#endif diff --git a/external/badvpn_dns/ncd-request/CMakeLists.txt b/external/badvpn_dns/ncd-request/CMakeLists.txt new file mode 100644 index 00000000..61447fd6 --- /dev/null +++ b/external/badvpn_dns/ncd-request/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(badvpn-ncd-request + ncd-request.c +) +target_link_libraries(badvpn-ncd-request ncdrequest ncdvalgenerator ncdvalparser) + +install( + TARGETS badvpn-ncd-request + RUNTIME DESTINATION bin +) diff --git a/external/badvpn_dns/ncd-request/ncd-request.c b/external/badvpn_dns/ncd-request/ncd-request.c new file mode 100644 index 00000000..5b44bdb2 --- /dev/null +++ b/external/badvpn_dns/ncd-request/ncd-request.c @@ -0,0 +1,224 @@ +/** + * @file ncd-request.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void client_handler_error (void *user); +static void client_handler_connected (void *user); +static void request_handler_sent (void *user); +static void request_handler_reply (void *user, NCDValMem reply_mem, NCDValRef reply_value); +static void request_handler_finished (void *user, int is_error); +static int write_all (int fd, const uint8_t *data, size_t len); +static int make_connect_addr (const char *str, struct BConnection_addr *out_addr); + +NCDValMem request_mem; +NCDValRef request_value; +BReactor reactor; +NCDRequestClient client; +NCDRequestClientRequest request; +int have_request; + +int main (int argc, char *argv[]) +{ + int res = 1; + + if (argc != 3) { + fprintf(stderr, "Usage: %s < unix: / tcp:
: > \n", (argc > 0 ? argv[0] : "")); + goto fail0; + } + + char *connect_address = argv[1]; + char *request_payload_string = argv[2]; + + BLog_InitStderr(); + + BTime_Init(); + + NCDValMem_Init(&request_mem); + + if (!NCDValParser_Parse(request_payload_string, strlen(request_payload_string), &request_mem, &request_value)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_Init failed"); + goto fail1; + } + + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + struct BConnection_addr addr; + if (!make_connect_addr(connect_address, &addr)) { + goto fail2; + } + + if (!NCDRequestClient_Init(&client, addr, &reactor, NULL, client_handler_error, client_handler_connected)) { + BLog(BLOG_ERROR, "NCDRequestClient_Init failed"); + goto fail2; + } + + have_request = 0; + + res = BReactor_Exec(&reactor); + + if (have_request) { + NCDRequestClientRequest_Free(&request); + } + NCDRequestClient_Free(&client); +fail2: + BReactor_Free(&reactor); +fail1: + NCDValMem_Free(&request_mem); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + return res; +} + +static int make_connect_addr (const char *str, struct BConnection_addr *out_addr) +{ + size_t i; + + if (i = string_begins_with(str, "unix:")) { + *out_addr = BConnection_addr_unix(str + i, strlen(str + i)); + } + else if (i = string_begins_with(str, "tcp:")) { + BAddr baddr; + if (!BAddr_Parse2(&baddr, (char *)str + i, NULL, 0, 1)) { + BLog(BLOG_ERROR, "failed to parse tcp address"); + return 0; + } + + *out_addr = BConnection_addr_baddr(baddr); + } + else { + BLog(BLOG_ERROR, "address must start with unix: or tcp:"); + return 0; + } + + return 1; +} + +static void client_handler_error (void *user) +{ + BLog(BLOG_ERROR, "client error"); + + BReactor_Quit(&reactor, 1); +} + +static void client_handler_connected (void *user) +{ + ASSERT(!have_request) + + if (!NCDRequestClientRequest_Init(&request, &client, request_value, NULL, request_handler_sent, request_handler_reply, request_handler_finished)) { + BLog(BLOG_ERROR, "NCDRequestClientRequest_Init failed"); + BReactor_Quit(&reactor, 1); + return; + } + + have_request = 1; +} + +static void request_handler_sent (void *user) +{ + ASSERT(have_request) +} + +static void request_handler_reply (void *user, NCDValMem reply_mem, NCDValRef reply_value) +{ + ASSERT(have_request) + + char *str = NCDValGenerator_Generate(reply_value); + if (!str) { + BLog(BLOG_ERROR, "NCDValGenerator_Generate failed"); + goto fail0; + } + + if (!write_all(1, (uint8_t *)str, strlen(str))) { + goto fail1; + } + if (!write_all(1, (const uint8_t *)"\n", 1)) { + goto fail1; + } + + free(str); + NCDValMem_Free(&reply_mem); + return; + +fail1: + free(str); +fail0: + NCDValMem_Free(&reply_mem); + BReactor_Quit(&reactor, 1); +} + +static void request_handler_finished (void *user, int is_error) +{ + if (is_error) { + BLog(BLOG_ERROR, "request error"); + BReactor_Quit(&reactor, 1); + return; + } + + BReactor_Quit(&reactor, 0); +} + +static int write_all (int fd, const uint8_t *data, size_t len) +{ + while (len > 0) { + ssize_t res = write(fd, data, len); + if (res <= 0) { + BLog(BLOG_ERROR, "write failed"); + return 0; + } + data += res; + len -= res; + } + + return 1; +} diff --git a/external/badvpn_dns/ncd/CMakeLists.txt b/external/badvpn_dns/ncd/CMakeLists.txt new file mode 100644 index 00000000..8c384aed --- /dev/null +++ b/external/badvpn_dns/ncd/CMakeLists.txt @@ -0,0 +1,211 @@ +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(NCD_ADDITIONAL_SOURCES) +set(NCD_ADDITIONAL_LIBS) + +if (NOT EMSCRIPTEN) + if (BADVPN_USE_LINUX_RFKILL) + list(APPEND NCD_ADDITIONAL_SOURCES + extra/NCDRfkillMonitor.c + modules/net_backend_rfkill.c + ) + endif () + + if (BADVPN_USE_LINUX_INPUT) + list(APPEND NCD_ADDITIONAL_SOURCES + modules/sys_evdev.c + ) + endif () + + if (BADVPN_USE_INOTIFY) + list(APPEND NCD_ADDITIONAL_SOURCES + modules/sys_watch_directory.c + ) + endif () + + badvpn_add_library(ncdinterfacemonitor "base;system" "" extra/NCDInterfaceMonitor.c) + + badvpn_add_library(ncdrequest "base;system;ncdvalgenerator;ncdvalparser" "" extra/NCDRequestClient.c) + + list(APPEND NCD_ADDITIONAL_SOURCES + extra/NCDIfConfig.c + extra/build_cmdline.c + extra/NCDBProcessOpts.c + modules/command_template.c + modules/event_template.c + modules/regex_match.c + modules/run.c + modules/runonce.c + modules/daemon.c + modules/net_backend_waitdevice.c + modules/net_backend_waitlink.c + modules/net_backend_badvpn.c + modules/net_backend_wpa_supplicant.c + modules/net_up.c + modules/net_dns.c + modules/net_iptables.c + modules/net_ipv4_addr.c + modules/net_ipv4_route.c + modules/net_ipv4_dhcp.c + modules/net_ipv4_arp_probe.c + modules/net_watch_interfaces.c + modules/sys_watch_input.c + modules/sys_watch_usb.c + modules/sys_request_server.c + modules/net_ipv6_wait_dynamic_addr.c + modules/sys_request_client.c + modules/reboot.c + modules/net_ipv6_addr.c + modules/net_ipv6_route.c + modules/socket.c + modules/sys_start_process.c + modules/load_module.c + ) + + list(APPEND NCD_ADDITIONAL_LIBS + dhcpclient arpprobe ncdinterfacemonitor ncdrequest udevmonitor badvpn_random dl + ) +endif () + +badvpn_add_library(ncdtokenizer "base" "" NCDConfigTokenizer.c) + +badvpn_add_library(ncdstringindex "base" "" NCDStringIndex.c) + +badvpn_add_library(ncdval "base;ncdstringindex" "" NCDVal.c) + +badvpn_add_library(ncdvalgenerator "base;ncdval" "" NCDValGenerator.c) + +badvpn_add_library(ncdvalparser "base;ncdval;ncdtokenizer;ncdvalcons" "" NCDValParser.c) + +badvpn_add_library(ncdast "" "" NCDAst.c) + +badvpn_add_library(ncdconfigparser "base;ncdtokenizer;ncdast" "" NCDConfigParser.c) + +badvpn_add_library(ncdsugar "ncdast" "" NCDSugar.c) + +badvpn_add_library(ncdvalcons "ncdval" "" NCDValCons.c) + +badvpn_add_library(ncdbuildprogram "base;ncdast;ncdconfigparser" "" NCDBuildProgram.c) + +badvpn_add_library(ncdobject "" "" NCDObject.c) + +badvpn_add_library(ncdmodule "base;ncdobject;ncdstringindex;ncdval" "" NCDModule.c) + +set(NCDINTERPRETER_SOURCES + NCDInterpreter.c + NCDModuleIndex.c + NCDInterpProcess.c + NCDInterpProg.c + NCDPlaceholderDb.c + NCDMethodIndex.c + extra/BEventLock.c + extra/NCDBuf.c + modules/var.c + modules/list.c + modules/depend.c + modules/multidepend.c + modules/dynamic_depend.c + modules/concat.c + modules/if.c + modules/strcmp.c + modules/logical.c + modules/sleep.c + modules/print.c + modules/blocker.c + modules/spawn.c + modules/imperative.c + modules/ref.c + modules/index.c + modules/alias.c + modules/process_manager.c + modules/ondemand.c + modules/foreach.c + modules/choose.c + modules/from_string.c + modules/to_string.c + modules/value.c + modules/try.c + modules/exit.c + modules/getargs.c + modules/arithmetic.c + modules/parse.c + modules/valuemetic.c + modules/file.c + modules/netmask.c + modules/implode.c + modules/call2.c + modules/assert.c + modules/explode.c + modules/net_ipv4_addr_in_network.c + modules/net_ipv6_addr_in_network.c + modules/timer.c + modules/file_open.c + modules/backtrack.c + modules/depend_scope.c + modules/substr.c + modules/log.c + modules/buffer.c + modules/getenv.c + ${NCD_ADDITIONAL_SOURCES} +) +set(NCDINTERPRETER_LIBS + base system flow flowextra ncdval ncdstringindex ncdvalgenerator ncdvalparser + ncdconfigparser ncdsugar ncdobject ncdmodule ${NCD_ADDITIONAL_LIBS}) +badvpn_add_library(ncdinterpreter "${NCDINTERPRETER_LIBS}" "" "${NCDINTERPRETER_SOURCES}") + +if (BADVPN_USE_LINUX_INPUT) + string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}") + execute_process(COMMAND ${CMAKE_C_COMPILER} ${FLAGS_LIST} -E ${CMAKE_CURRENT_SOURCE_DIR}/include_linux_input.c + RESULT_VARIABLE LINUX_INPUT_PREPROCESS_RESULT + OUTPUT_VARIABLE LINUX_INPUT_PREPROCESS_OUTPUT) + if (NOT LINUX_INPUT_PREPROCESS_RESULT EQUAL 0) + message(FATAL_ERROR "failed to preprocess linux/input.h include") + endif () + + string(REGEX MATCH "\"(/[^\"]+/linux/input.h)\"" LINUX_INPUT_MATCH ${LINUX_INPUT_PREPROCESS_OUTPUT}) + if (NOT LINUX_INPUT_MATCH) + message(FATAL_ERROR "failed to match preprocessor output for path of linux/input.h") + endif () + set(LINUX_INPUT_H_PATH ${CMAKE_MATCH_1}) + + message(STATUS "Generating linux_input_names.h from ${LINUX_INPUT_H_PATH}") + + execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/parse_linux_input.sh + ${LINUX_INPUT_H_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/linux_input_names.h + RESULT_VARIABLE LINUX_INPUT_PARSE_RESULT) + if (NOT LINUX_INPUT_PARSE_RESULT EQUAL 0) + message(FATAL_ERROR "failed to generate linux_input_names.h") + endif () +endif () + +if (NOT EMSCRIPTEN) + add_executable(badvpn-ncd ncd.c) + target_link_libraries(badvpn-ncd ncdinterpreter ncdbuildprogram) + + install( + TARGETS badvpn-ncd + RUNTIME DESTINATION bin + ) +endif () + +if (EMSCRIPTEN) + add_executable(emncd emncd.c) + target_link_libraries(emncd ncdinterpreter) + + add_custom_command( + OUTPUT emncd.bc + DEPENDS emncd + COMMAND cp emncd emncd.bc + ) + + add_custom_command( + OUTPUT emncd.js + DEPENDS emncd.bc + COMMAND + ${CMAKE_C_COMPILER} emncd.bc -o emncd.js -O2 + -s EXPORTED_FUNCTIONS=\"['_breactor_timer_cb','_main','_emncd_start','_emncd_stop']\" + ) + + add_custom_target(emncd_js ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/emncd.js) +endif () diff --git a/external/badvpn_dns/ncd/NCDAst.c b/external/badvpn_dns/ncd/NCDAst.c new file mode 100644 index 00000000..2b229e3f --- /dev/null +++ b/external/badvpn_dns/ncd/NCDAst.c @@ -0,0 +1,1022 @@ +/** + * @file NCDAst.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include "NCDAst.h" + +struct NCDValue__list_element { + LinkedList1Node list_node; + NCDValue v; +}; + +struct NCDValue__map_element { + LinkedList1Node list_node; + NCDValue key; + NCDValue val; +}; + +struct ProgramElem { + LinkedList1Node elems_list_node; + NCDProgramElem elem; +}; + +struct BlockStatement { + LinkedList1Node statements_list_node; + NCDStatement s; +}; + +struct IfBlockIf { + LinkedList1Node ifs_list_node; + NCDIf ifc; +}; + +static void value_assert (NCDValue *o) +{ + switch (o->type) { + case NCDVALUE_STRING: + case NCDVALUE_LIST: + case NCDVALUE_MAP: + case NCDVALUE_VAR: + return; + default: + ASSERT(0); + } +} + +void NCDValue_Free (NCDValue *o) +{ + switch (o->type) { + case NCDVALUE_STRING: { + free(o->string); + } break; + + case NCDVALUE_LIST: { + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->list)) { + struct NCDValue__list_element *e = UPPER_OBJECT(n, struct NCDValue__list_element, list_node); + + NCDValue_Free(&e->v); + LinkedList1_Remove(&o->list, &e->list_node); + free(e); + } + } break; + + case NCDVALUE_MAP: { + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->map_list)) { + struct NCDValue__map_element *e = UPPER_OBJECT(n, struct NCDValue__map_element, list_node); + + LinkedList1_Remove(&o->map_list, &e->list_node); + NCDValue_Free(&e->key); + NCDValue_Free(&e->val); + free(e); + } + } break; + + case NCDVALUE_VAR: { + free(o->var_name); + } break; + + default: + ASSERT(0); + } +} + +int NCDValue_Type (NCDValue *o) +{ + value_assert(o); + + return o->type; +} + +int NCDValue_InitString (NCDValue *o, const char *str) +{ + return NCDValue_InitStringBin(o, (const uint8_t *)str, strlen(str)); +} + +int NCDValue_InitStringBin (NCDValue *o, const uint8_t *str, size_t len) +{ + if (len == SIZE_MAX) { + return 0; + } + + if (!(o->string = malloc(len + 1))) { + return 0; + } + + memcpy(o->string, str, len); + o->string[len] = '\0'; + o->string_len = len; + + o->type = NCDVALUE_STRING; + + return 1; +} + +const char * NCDValue_StringValue (NCDValue *o) +{ + ASSERT(o->type == NCDVALUE_STRING) + + return (char *)o->string; +} + +size_t NCDValue_StringLength (NCDValue *o) +{ + ASSERT(o->type == NCDVALUE_STRING) + + return o->string_len; +} + +void NCDValue_InitList (NCDValue *o) +{ + o->type = NCDVALUE_LIST; + LinkedList1_Init(&o->list); + o->list_count = 0; +} + +size_t NCDValue_ListCount (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + return o->list_count; +} + +int NCDValue_ListAppend (NCDValue *o, NCDValue v) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + value_assert(&v); + + if (o->list_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__list_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->v = v; + LinkedList1_Append(&o->list, &e->list_node); + + o->list_count++; + + return 1; +} + +int NCDValue_ListPrepend (NCDValue *o, NCDValue v) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + value_assert(&v); + + if (o->list_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__list_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->v = v; + LinkedList1_Prepend(&o->list, &e->list_node); + + o->list_count++; + + return 1; +} + +NCDValue * NCDValue_ListFirst (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + LinkedList1Node *ln = LinkedList1_GetFirst(&o->list); + + if (!ln) { + return NULL; + } + + struct NCDValue__list_element *e = UPPER_OBJECT(ln, struct NCDValue__list_element, list_node); + + return &e->v; +} + +NCDValue * NCDValue_ListNext (NCDValue *o, NCDValue *ev) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_LIST) + + struct NCDValue__list_element *cur_e = UPPER_OBJECT(ev, struct NCDValue__list_element, v); + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->list_node); + + if (!ln) { + return NULL; + } + + struct NCDValue__list_element *e = UPPER_OBJECT(ln, struct NCDValue__list_element, list_node); + + return &e->v; +} + +void NCDValue_InitMap (NCDValue *o) +{ + o->type = NCDVALUE_MAP; + LinkedList1_Init(&o->map_list); + o->map_count = 0; +} + +size_t NCDValue_MapCount (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + return o->map_count; +} + +int NCDValue_MapPrepend (NCDValue *o, NCDValue key, NCDValue val) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + value_assert(&key); + value_assert(&val); + + if (o->map_count == SIZE_MAX) { + return 0; + } + + struct NCDValue__map_element *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + e->key = key; + e->val = val; + LinkedList1_Prepend(&o->map_list, &e->list_node); + + o->map_count++; + + return 1; +} + +NCDValue * NCDValue_MapFirstKey (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + LinkedList1Node *ln = LinkedList1_GetFirst(&o->map_list); + + if (!ln) { + return NULL; + } + + struct NCDValue__map_element *e = UPPER_OBJECT(ln, struct NCDValue__map_element, list_node); + + value_assert(&e->key); + value_assert(&e->val); + + return &e->key; +} + +NCDValue * NCDValue_MapNextKey (NCDValue *o, NCDValue *ekey) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + struct NCDValue__map_element *e0 = UPPER_OBJECT(ekey, struct NCDValue__map_element, key); + value_assert(&e0->key); + value_assert(&e0->val); + + LinkedList1Node *ln = LinkedList1Node_Next(&e0->list_node); + + if (!ln) { + return NULL; + } + + struct NCDValue__map_element *e = UPPER_OBJECT(ln, struct NCDValue__map_element, list_node); + + value_assert(&e->key); + value_assert(&e->val); + + return &e->key; +} + +NCDValue * NCDValue_MapKeyValue (NCDValue *o, NCDValue *ekey) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_MAP) + + struct NCDValue__map_element *e = UPPER_OBJECT(ekey, struct NCDValue__map_element, key); + value_assert(&e->key); + value_assert(&e->val); + + return &e->val; +} + +int NCDValue_InitVar (NCDValue *o, const char *var_name) +{ + ASSERT(var_name) + + if (!(o->var_name = strdup(var_name))) { + return 0; + } + + o->type = NCDVALUE_VAR; + + return 1; +} + +const char * NCDValue_VarName (NCDValue *o) +{ + value_assert(o); + ASSERT(o->type == NCDVALUE_VAR) + + return o->var_name; +} + +void NCDProgram_Init (NCDProgram *o) +{ + LinkedList1_Init(&o->elems_list); + o->num_elems = 0; +} + +void NCDProgram_Free (NCDProgram *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->elems_list)) { + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + NCDProgramElem_Free(&e->elem); + LinkedList1_Remove(&o->elems_list, &e->elems_list_node); + free(e); + } +} + +NCDProgramElem * NCDProgram_PrependElem (NCDProgram *o, NCDProgramElem elem) +{ + if (o->num_elems == SIZE_MAX) { + return NULL; + } + + struct ProgramElem *e = malloc(sizeof(*e)); + if (!e) { + return NULL; + } + + LinkedList1_Prepend(&o->elems_list, &e->elems_list_node); + e->elem = elem; + + o->num_elems++; + + return &e->elem; +} + +NCDProgramElem * NCDProgram_FirstElem (NCDProgram *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->elems_list); + if (!ln) { + return NULL; + } + + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + + return &e->elem; +} + +NCDProgramElem * NCDProgram_NextElem (NCDProgram *o, NCDProgramElem *ee) +{ + ASSERT(ee) + + struct ProgramElem *cur_e = UPPER_OBJECT(ee, struct ProgramElem, elem); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->elems_list_node); + if (!ln) { + return NULL; + } + + struct ProgramElem *e = UPPER_OBJECT(ln, struct ProgramElem, elems_list_node); + + return &e->elem; +} + +size_t NCDProgram_NumElems (NCDProgram *o) +{ + return o->num_elems; +} + +int NCDProgram_ContainsElemType (NCDProgram *o, int elem_type) +{ + for (NCDProgramElem *elem = NCDProgram_FirstElem(o); elem; elem = NCDProgram_NextElem(o, elem)) { + if (NCDProgramElem_Type(elem) == elem_type) { + return 1; + } + } + + return 0; +} + +void NCDProgram_RemoveElem (NCDProgram *o, NCDProgramElem *ee) +{ + ASSERT(ee) + + struct ProgramElem *e = UPPER_OBJECT(ee, struct ProgramElem, elem); + NCDProgramElem_Free(&e->elem); + LinkedList1_Remove(&o->elems_list, &e->elems_list_node); + free(e); + + ASSERT(o->num_elems > 0) + o->num_elems--; +} + +int NCDProgram_ReplaceElemWithProgram (NCDProgram *o, NCDProgramElem *ee, NCDProgram replace_prog) +{ + ASSERT(ee) + + if (replace_prog.num_elems > SIZE_MAX - o->num_elems) { + return 0; + } + + struct ProgramElem *e = UPPER_OBJECT(ee, struct ProgramElem, elem); + + LinkedList1_InsertListAfter(&o->elems_list, replace_prog.elems_list, &e->elems_list_node); + o->num_elems += replace_prog.num_elems; + + NCDProgram_RemoveElem(o, ee); + + return 1; +} + +void NCDProgramElem_InitProcess (NCDProgramElem *o, NCDProcess process) +{ + o->type = NCDPROGRAMELEM_PROCESS; + o->process = process; +} + +int NCDProgramElem_InitInclude (NCDProgramElem *o, const char *path_data, size_t path_length) +{ + if (!(o->include.path_data = b_strdup_bin(path_data, path_length))) { + return 0; + } + + o->type = NCDPROGRAMELEM_INCLUDE; + o->include.path_length = path_length; + + return 1; +} + +int NCDProgramElem_InitIncludeGuard (NCDProgramElem *o, const char *id_data, size_t id_length) +{ + if (!(o->include_guard.id_data = b_strdup_bin(id_data, id_length))) { + return 0; + } + + o->type = NCDPROGRAMELEM_INCLUDE_GUARD; + o->include_guard.id_length = id_length; + + return 1; +} + + +void NCDProgramElem_Free (NCDProgramElem *o) +{ + switch (o->type) { + case NCDPROGRAMELEM_PROCESS: { + NCDProcess_Free(&o->process); + } break; + + case NCDPROGRAMELEM_INCLUDE: { + free(o->include.path_data); + } break; + + case NCDPROGRAMELEM_INCLUDE_GUARD: { + free(o->include_guard.id_data); + } break; + + default: ASSERT(0); + } +} + +int NCDProgramElem_Type (NCDProgramElem *o) +{ + return o->type; +} + +NCDProcess * NCDProgramElem_Process (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_PROCESS) + + return &o->process; +} + +const char * NCDProgramElem_IncludePathData (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE) + + return o->include.path_data; +} + +size_t NCDProgramElem_IncludePathLength (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE) + + return o->include.path_length; +} + +const char * NCDProgramElem_IncludeGuardIdData (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE_GUARD) + + return o->include_guard.id_data; +} + +size_t NCDProgramElem_IncludeGuardIdLength (NCDProgramElem *o) +{ + ASSERT(o->type == NCDPROGRAMELEM_INCLUDE_GUARD) + + return o->include_guard.id_length; +} + +int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block) +{ + ASSERT(is_template == !!is_template) + ASSERT(name) + + if (!(o->name = strdup(name))) { + return 0; + } + + o->is_template = is_template; + o->block = block; + + return 1; +} + +void NCDProcess_Free (NCDProcess *o) +{ + NCDBlock_Free(&o->block); + free(o->name); +} + +int NCDProcess_IsTemplate (NCDProcess *o) +{ + return o->is_template; +} + +const char * NCDProcess_Name (NCDProcess *o) +{ + return o->name; +} + +NCDBlock * NCDProcess_Block (NCDProcess *o) +{ + return &o->block; +} + +void NCDBlock_Init (NCDBlock *o) +{ + LinkedList1_Init(&o->statements_list); + o->count = 0; +} + +void NCDBlock_Free (NCDBlock *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->statements_list)) { + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + NCDStatement_Free(&e->s); + LinkedList1_Remove(&o->statements_list, &e->statements_list_node); + free(e); + } +} + +int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s) +{ + return NCDBlock_InsertStatementAfter(o, NULL, s); +} + +int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s) +{ + struct BlockStatement *after_e = NULL; + if (after) { + after_e = UPPER_OBJECT(after, struct BlockStatement, s); + } + + if (o->count == SIZE_MAX) { + return 0; + } + + struct BlockStatement *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + if (after_e) { + LinkedList1_InsertAfter(&o->statements_list, &e->statements_list_node, &after_e->statements_list_node); + } else { + LinkedList1_Prepend(&o->statements_list, &e->statements_list_node); + } + e->s = s; + + o->count++; + + return 1; +} + +NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s) +{ + ASSERT(es) + + struct BlockStatement *e = UPPER_OBJECT(es, struct BlockStatement, s); + + NCDStatement_Free(&e->s); + e->s = s; + + return &e->s; +} + +NCDStatement * NCDBlock_FirstStatement (NCDBlock *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->statements_list); + if (!ln) { + return NULL; + } + + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + + return &e->s; +} + +NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es) +{ + ASSERT(es) + + struct BlockStatement *cur_e = UPPER_OBJECT(es, struct BlockStatement, s); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->statements_list_node); + if (!ln) { + return NULL; + } + + struct BlockStatement *e = UPPER_OBJECT(ln, struct BlockStatement, statements_list_node); + + return &e->s; +} + +size_t NCDBlock_NumStatements (NCDBlock *o) +{ + return o->count; +} + +int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args) +{ + ASSERT(cmdname) + ASSERT(NCDValue_Type(&args) == NCDVALUE_LIST) + + o->name = NULL; + o->reg.objname = NULL; + o->reg.cmdname = NULL; + + if (name && !(o->name = strdup(name))) { + goto fail; + } + + if (objname && !(o->reg.objname = strdup(objname))) { + goto fail; + } + + if (!(o->reg.cmdname = strdup(cmdname))) { + goto fail; + } + + o->type = NCDSTATEMENT_REG; + o->reg.args = args; + + return 1; + +fail: + free(o->name); + free(o->reg.objname); + free(o->reg.cmdname); + return 0; +} + +int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock) +{ + o->name = NULL; + + if (name && !(o->name = strdup(name))) { + return 0; + } + + o->type = NCDSTATEMENT_IF; + o->ifc.ifblock = ifblock; + o->ifc.have_else = 0; + + return 1; +} + +int NCDStatement_InitForeach (NCDStatement *o, const char *name, NCDValue collection, const char *name1, const char *name2, NCDBlock block) +{ + ASSERT(name1) + + o->name = NULL; + o->foreach.name1 = NULL; + o->foreach.name2 = NULL; + + if (name && !(o->name = strdup(name))) { + goto fail; + } + + if (!(o->foreach.name1 = strdup(name1))) { + goto fail; + } + + if (name2 && !(o->foreach.name2 = strdup(name2))) { + goto fail; + } + + o->type = NCDSTATEMENT_FOREACH; + o->foreach.collection = collection; + o->foreach.block = block; + o->foreach.is_grabbed = 0; + + return 1; + +fail: + free(o->name); + free(o->foreach.name1); + free(o->foreach.name2); + return 0; +} + +void NCDStatement_Free (NCDStatement *o) +{ + switch (o->type) { + case NCDSTATEMENT_REG: { + NCDValue_Free(&o->reg.args); + free(o->reg.cmdname); + free(o->reg.objname); + } break; + + case NCDSTATEMENT_IF: { + if (o->ifc.have_else) { + NCDBlock_Free(&o->ifc.else_block); + } + + NCDIfBlock_Free(&o->ifc.ifblock); + } break; + + case NCDSTATEMENT_FOREACH: { + if (!o->foreach.is_grabbed) { + NCDBlock_Free(&o->foreach.block); + NCDValue_Free(&o->foreach.collection); + } + free(o->foreach.name2); + free(o->foreach.name1); + } break; + + default: ASSERT(0); + } + + free(o->name); +} + +int NCDStatement_Type (NCDStatement *o) +{ + return o->type; +} + +const char * NCDStatement_Name (NCDStatement *o) +{ + return o->name; +} + +const char * NCDStatement_RegObjName (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return o->reg.objname; +} + +const char * NCDStatement_RegCmdName (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return o->reg.cmdname; +} + +NCDValue * NCDStatement_RegArgs (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_REG) + + return &o->reg.args; +} + +NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + + return &o->ifc.ifblock; +} + +void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + ASSERT(!o->ifc.have_else) + + o->ifc.have_else = 1; + o->ifc.else_block = else_block; +} + +NCDBlock * NCDStatement_IfElse (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + + if (!o->ifc.have_else) { + return NULL; + } + + return &o->ifc.else_block; +} + +NCDBlock NCDStatement_IfGrabElse (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_IF) + ASSERT(o->ifc.have_else) + + o->ifc.have_else = 0; + + return o->ifc.else_block; +} + +NCDValue * NCDStatement_ForeachCollection (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + return &o->foreach.collection; +} + +const char * NCDStatement_ForeachName1 (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + + return o->foreach.name1; +} + +const char * NCDStatement_ForeachName2 (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + + return o->foreach.name2; +} + +NCDBlock * NCDStatement_ForeachBlock (NCDStatement *o) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + return &o->foreach.block; +} + +void NCDStatement_ForeachGrab (NCDStatement *o, NCDValue *out_collection, NCDBlock *out_block) +{ + ASSERT(o->type == NCDSTATEMENT_FOREACH) + ASSERT(!o->foreach.is_grabbed) + + *out_collection = o->foreach.collection; + *out_block = o->foreach.block; + o->foreach.is_grabbed = 1; +} + +void NCDIfBlock_Init (NCDIfBlock *o) +{ + LinkedList1_Init(&o->ifs_list); +} + +void NCDIfBlock_Free (NCDIfBlock *o) +{ + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->ifs_list)) { + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + NCDIf_Free(&e->ifc); + LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node); + free(e); + } +} + +int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc) +{ + struct IfBlockIf *e = malloc(sizeof(*e)); + if (!e) { + return 0; + } + + LinkedList1_Prepend(&o->ifs_list, &e->ifs_list_node); + e->ifc = ifc; + + return 1; +} + +NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o) +{ + LinkedList1Node *ln = LinkedList1_GetFirst(&o->ifs_list); + if (!ln) { + return NULL; + } + + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + + return &e->ifc; +} + +NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei) +{ + ASSERT(ei) + + struct IfBlockIf *cur_e = UPPER_OBJECT(ei, struct IfBlockIf, ifc); + + LinkedList1Node *ln = LinkedList1Node_Next(&cur_e->ifs_list_node); + if (!ln) { + return NULL; + } + + struct IfBlockIf *e = UPPER_OBJECT(ln, struct IfBlockIf, ifs_list_node); + + return &e->ifc; +} + +NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei) +{ + ASSERT(ei) + + struct IfBlockIf *e = UPPER_OBJECT(ei, struct IfBlockIf, ifc); + + NCDIf old_ifc = e->ifc; + + LinkedList1_Remove(&o->ifs_list, &e->ifs_list_node); + free(e); + + return old_ifc; +} + +void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block) +{ + o->cond = cond; + o->block = block; +} + +void NCDIf_Free (NCDIf *o) +{ + NCDValue_Free(&o->cond); + NCDBlock_Free(&o->block); +} + +void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block) +{ + *out_cond = o->cond; + *out_block = o->block; +} + +NCDValue * NCDIf_Cond (NCDIf *o) +{ + return &o->cond; +} + +NCDBlock * NCDIf_Block (NCDIf *o) +{ + return &o->block; +} diff --git a/external/badvpn_dns/ncd/NCDAst.h b/external/badvpn_dns/ncd/NCDAst.h new file mode 100644 index 00000000..95ca9e44 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDAst.h @@ -0,0 +1,237 @@ +/** + * @file NCDAst.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDAST_H +#define BADVPN_NCDAST_H + +#include +#include + +#include +#include + +typedef struct NCDValue_s NCDValue; +typedef struct NCDProgram_s NCDProgram; +typedef struct NCDProgramElem_s NCDProgramElem; +typedef struct NCDProcess_s NCDProcess; +typedef struct NCDBlock_s NCDBlock; +typedef struct NCDStatement_s NCDStatement; +typedef struct NCDIfBlock_s NCDIfBlock; +typedef struct NCDIf_s NCDIf; + +struct NCDValue_s { + int type; + union { + struct { + uint8_t *string; + size_t string_len; + }; + struct { + LinkedList1 list; + size_t list_count; + }; + struct { + LinkedList1 map_list; + size_t map_count; + }; + struct { + char *var_name; + }; + }; +}; + +struct NCDProgram_s { + LinkedList1 elems_list; + size_t num_elems; +}; + +struct NCDBlock_s { + LinkedList1 statements_list; + size_t count; +}; + +struct NCDProcess_s { + int is_template; + char *name; + NCDBlock block; +}; + +struct NCDProgramElem_s { + int type; + union { + NCDProcess process; + struct { + char *path_data; + size_t path_length; + } include; + struct { + char *id_data; + size_t id_length; + } include_guard; + }; +}; + +struct NCDIfBlock_s { + LinkedList1 ifs_list; +}; + +struct NCDStatement_s { + int type; + char *name; + union { + struct { + char *objname; + char *cmdname; + NCDValue args; + } reg; + struct { + NCDIfBlock ifblock; + int have_else; + NCDBlock else_block; + } ifc; + struct { + NCDValue collection; + char *name1; + char *name2; + NCDBlock block; + int is_grabbed; + } foreach; + }; +}; + +struct NCDIf_s { + NCDValue cond; + NCDBlock block; +}; + +// + +#define NCDVALUE_STRING 1 +#define NCDVALUE_LIST 2 +#define NCDVALUE_MAP 3 +#define NCDVALUE_VAR 4 + +#define NCDPROGRAMELEM_PROCESS 1 +#define NCDPROGRAMELEM_INCLUDE 2 +#define NCDPROGRAMELEM_INCLUDE_GUARD 3 + +#define NCDSTATEMENT_REG 1 +#define NCDSTATEMENT_IF 2 +#define NCDSTATEMENT_FOREACH 3 + +void NCDValue_Free (NCDValue *o); +int NCDValue_Type (NCDValue *o); +int NCDValue_InitString (NCDValue *o, const char *str) WARN_UNUSED; +int NCDValue_InitStringBin (NCDValue *o, const uint8_t *str, size_t len) WARN_UNUSED; +const char * NCDValue_StringValue (NCDValue *o); +size_t NCDValue_StringLength (NCDValue *o); +void NCDValue_InitList (NCDValue *o); +size_t NCDValue_ListCount (NCDValue *o); +int NCDValue_ListAppend (NCDValue *o, NCDValue v) WARN_UNUSED; +int NCDValue_ListPrepend (NCDValue *o, NCDValue v) WARN_UNUSED; +NCDValue * NCDValue_ListFirst (NCDValue *o); +NCDValue * NCDValue_ListNext (NCDValue *o, NCDValue *ev); +void NCDValue_InitMap (NCDValue *o); +size_t NCDValue_MapCount (NCDValue *o); +int NCDValue_MapPrepend (NCDValue *o, NCDValue key, NCDValue val) WARN_UNUSED; +NCDValue * NCDValue_MapFirstKey (NCDValue *o); +NCDValue * NCDValue_MapNextKey (NCDValue *o, NCDValue *ekey); +NCDValue * NCDValue_MapKeyValue (NCDValue *o, NCDValue *ekey); +int NCDValue_InitVar (NCDValue *o, const char *var_name) WARN_UNUSED; +const char * NCDValue_VarName (NCDValue *o); + +void NCDProgram_Init (NCDProgram *o); +void NCDProgram_Free (NCDProgram *o); +NCDProgramElem * NCDProgram_PrependElem (NCDProgram *o, NCDProgramElem elem) WARN_UNUSED; +NCDProgramElem * NCDProgram_FirstElem (NCDProgram *o); +NCDProgramElem * NCDProgram_NextElem (NCDProgram *o, NCDProgramElem *ee); +size_t NCDProgram_NumElems (NCDProgram *o); +int NCDProgram_ContainsElemType (NCDProgram *o, int elem_type); +void NCDProgram_RemoveElem (NCDProgram *o, NCDProgramElem *ee); +int NCDProgram_ReplaceElemWithProgram (NCDProgram *o, NCDProgramElem *ee, NCDProgram replace_prog) WARN_UNUSED; + +void NCDProgramElem_InitProcess (NCDProgramElem *o, NCDProcess process); +int NCDProgramElem_InitInclude (NCDProgramElem *o, const char *path_data, size_t path_length) WARN_UNUSED; +int NCDProgramElem_InitIncludeGuard (NCDProgramElem *o, const char *id_data, size_t id_length) WARN_UNUSED; +void NCDProgramElem_Free (NCDProgramElem *o); +int NCDProgramElem_Type (NCDProgramElem *o); +NCDProcess * NCDProgramElem_Process (NCDProgramElem *o); +const char * NCDProgramElem_IncludePathData (NCDProgramElem *o); +size_t NCDProgramElem_IncludePathLength (NCDProgramElem *o); +const char * NCDProgramElem_IncludeGuardIdData (NCDProgramElem *o); +size_t NCDProgramElem_IncludeGuardIdLength (NCDProgramElem *o); + +int NCDProcess_Init (NCDProcess *o, int is_template, const char *name, NCDBlock block) WARN_UNUSED; +void NCDProcess_Free (NCDProcess *o); +int NCDProcess_IsTemplate (NCDProcess *o); +const char * NCDProcess_Name (NCDProcess *o); +NCDBlock * NCDProcess_Block (NCDProcess *o); + +void NCDBlock_Init (NCDBlock *o); +void NCDBlock_Free (NCDBlock *o); +int NCDBlock_PrependStatement (NCDBlock *o, NCDStatement s) WARN_UNUSED; +int NCDBlock_InsertStatementAfter (NCDBlock *o, NCDStatement *after, NCDStatement s) WARN_UNUSED; +NCDStatement * NCDBlock_ReplaceStatement (NCDBlock *o, NCDStatement *es, NCDStatement s); +NCDStatement * NCDBlock_FirstStatement (NCDBlock *o); +NCDStatement * NCDBlock_NextStatement (NCDBlock *o, NCDStatement *es); +size_t NCDBlock_NumStatements (NCDBlock *o); + +int NCDStatement_InitReg (NCDStatement *o, const char *name, const char *objname, const char *cmdname, NCDValue args) WARN_UNUSED; +int NCDStatement_InitIf (NCDStatement *o, const char *name, NCDIfBlock ifblock) WARN_UNUSED; +int NCDStatement_InitForeach (NCDStatement *o, const char *name, NCDValue collection, const char *name1, const char *name2, NCDBlock block) WARN_UNUSED; +void NCDStatement_Free (NCDStatement *o); +int NCDStatement_Type (NCDStatement *o); +const char * NCDStatement_Name (NCDStatement *o); +const char * NCDStatement_RegObjName (NCDStatement *o); +const char * NCDStatement_RegCmdName (NCDStatement *o); +NCDValue * NCDStatement_RegArgs (NCDStatement *o); +NCDIfBlock * NCDStatement_IfBlock (NCDStatement *o); +void NCDStatement_IfAddElse (NCDStatement *o, NCDBlock else_block); +NCDBlock * NCDStatement_IfElse (NCDStatement *o); +NCDBlock NCDStatement_IfGrabElse (NCDStatement *o); +NCDValue * NCDStatement_ForeachCollection (NCDStatement *o); +const char * NCDStatement_ForeachName1 (NCDStatement *o); +const char * NCDStatement_ForeachName2 (NCDStatement *o); +NCDBlock * NCDStatement_ForeachBlock (NCDStatement *o); +void NCDStatement_ForeachGrab (NCDStatement *o, NCDValue *out_collection, NCDBlock *out_block); + +void NCDIfBlock_Init (NCDIfBlock *o); +void NCDIfBlock_Free (NCDIfBlock *o); +int NCDIfBlock_PrependIf (NCDIfBlock *o, NCDIf ifc) WARN_UNUSED; +NCDIf * NCDIfBlock_FirstIf (NCDIfBlock *o); +NCDIf * NCDIfBlock_NextIf (NCDIfBlock *o, NCDIf *ei); +NCDIf NCDIfBlock_GrabIf (NCDIfBlock *o, NCDIf *ei); + +void NCDIf_Init (NCDIf *o, NCDValue cond, NCDBlock block); +void NCDIf_Free (NCDIf *o); +void NCDIf_FreeGrab (NCDIf *o, NCDValue *out_cond, NCDBlock *out_block); +NCDValue * NCDIf_Cond (NCDIf *o); +NCDBlock * NCDIf_Block (NCDIf *o); + +#endif diff --git a/external/badvpn_dns/ncd/NCDBuildProgram.c b/external/badvpn_dns/ncd/NCDBuildProgram.c new file mode 100644 index 00000000..941a1429 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDBuildProgram.c @@ -0,0 +1,316 @@ +/** + * @file NCDBuildProgram.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NCDBuildProgram.h" + +#include + +#define MAX_INCLUDE_DEPTH 32 + +struct guard { + char *id_data; + size_t id_length; + struct guard *next; +}; + +struct build_state { + struct guard *top_guard; +}; + +static int add_guard (struct guard **first, const char *id_data, size_t id_length) +{ + struct guard *g = malloc(sizeof(*g)); + if (!g) { + goto fail0; + } + + if (!(g->id_data = b_strdup_bin(id_data, id_length))) { + goto fail1; + } + + g->id_length = id_length; + + g->next = *first; + *first = g; + + return 1; + +fail1: + free(g); +fail0: + return 0; +} + +static void prepend_guards (struct guard **first, struct guard *guards) +{ + if (!guards) { + return; + } + + struct guard *last = guards; + while (last->next) { + last = last->next; + } + + last->next = *first; + *first = guards; +} + +static void free_guards (struct guard *g) +{ + while (g) { + struct guard *next_g = g->next; + free(g->id_data); + free(g); + g = next_g; + } +} + +static int guard_exists (struct guard *top_guard, const char *id_data, size_t id_length) +{ + for (struct guard *g = top_guard; g; g = g->next) { + if (g->id_length == id_length && !memcmp(g->id_data, id_data, id_length)) { + return 1; + } + } + + return 0; +} + +static char * make_dir_path (const char *file_path) +{ + int found_slash = 0; + size_t last_slash = 0; // initialize to remove warning + + for (size_t i = 0; file_path[i]; i++) { + if (file_path[i] == '/') { + found_slash = 1; + last_slash = i; + } + } + + char *dir_path; + + if (!found_slash) { + if (!file_path[0]) { + BLog(BLOG_ERROR, "file '%s': file path must not be empty", file_path); + return NULL; + } + dir_path = b_strdup(""); + } else { + if (!file_path[last_slash + 1]) { + BLog(BLOG_ERROR, "file '%s': file path must not end in a slash", file_path); + return NULL; + } + dir_path = b_strdup_bin(file_path, last_slash + 1); + } + + if (!dir_path) { + BLog(BLOG_ERROR, "file '%s': b_strdup/b_strdup_bin failed", file_path); + return NULL; + } + + return dir_path; +} + +static char * make_include_path (const char *file_path, const char *dir_path, const char *target, size_t target_len) +{ + ASSERT(target_len == strlen(target)) + + if (target_len == 0) { + BLog(BLOG_ERROR, "file '%s': include target must not be empty", file_path); + return NULL; + } + + if (target[target_len - 1] == '/') { + BLog(BLOG_ERROR, "file '%s': include target must not end in a slash", file_path); + return NULL; + } + + char *real_target; + + if (target[0] == '/') { + real_target = b_strdup(target); + } else { + real_target = concat_strings(2, dir_path, target); + } + + if (!real_target) { + BLog(BLOG_ERROR, "file '%s': b_strdup/concat_strings failed", file_path); + return NULL; + } + + return real_target; +} + +static int process_file (struct build_state *st, int depth, const char *file_path, NCDProgram *out_program, int *out_guarded) +{ + int ret_val = 0; + + if (depth > MAX_INCLUDE_DEPTH) { + BLog(BLOG_ERROR, "file '%s': maximum include depth (%d) exceeded (include cycle?)", file_path, (int)MAX_INCLUDE_DEPTH); + goto fail0; + } + + char *dir_path = make_dir_path(file_path); + if (!dir_path) { + goto fail0; + } + + uint8_t *data; + size_t len; + if (!read_file(file_path, &data, &len)) { + BLog(BLOG_ERROR, "file '%s': failed to read contents", file_path); + goto fail1; + } + + NCDProgram program; + int res = NCDConfigParser_Parse((char *)data, len, &program); + free(data); + if (!res) { + BLog(BLOG_ERROR, "file '%s': failed to parse", file_path); + goto fail1; + } + + struct guard *our_guards = NULL; + + NCDProgramElem *elem = NCDProgram_FirstElem(&program); + while (elem) { + NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem); + if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE_GUARD) { + elem = next_elem; + continue; + } + + const char *id_data = NCDProgramElem_IncludeGuardIdData(elem); + size_t id_length = NCDProgramElem_IncludeGuardIdLength(elem); + + if (guard_exists(st->top_guard, id_data, id_length)) { + *out_guarded = 1; + ret_val = 1; + goto fail2; + } + + if (!add_guard(&our_guards, id_data, id_length)) { + BLog(BLOG_ERROR, "file '%s': add_guard failed", file_path); + goto fail2; + } + + NCDProgram_RemoveElem(&program, elem); + elem = next_elem; + } + + prepend_guards(&st->top_guard, our_guards); + our_guards = NULL; + + elem = NCDProgram_FirstElem(&program); + while (elem) { + NCDProgramElem *next_elem = NCDProgram_NextElem(&program, elem); + if (NCDProgramElem_Type(elem) != NCDPROGRAMELEM_INCLUDE) { + elem = next_elem; + continue; + } + + const char *target = NCDProgramElem_IncludePathData(elem); + size_t target_len = NCDProgramElem_IncludePathLength(elem); + + if (strlen(target) != target_len) { + BLog(BLOG_ERROR, "file '%s': include path must not contain null characters", file_path); + goto fail2; + } + + char *real_target = make_include_path(file_path, dir_path, target, target_len); + if (!real_target) { + goto fail2; + } + + NCDProgram included_program; + int included_guarded; + int res = process_file(st, depth + 1, real_target, &included_program, &included_guarded); + free(real_target); + if (!res) { + goto fail2; + } + + if (included_guarded) { + NCDProgram_RemoveElem(&program, elem); + } else { + if (!NCDProgram_ReplaceElemWithProgram(&program, elem, included_program)) { + BLog(BLOG_ERROR, "file '%s': NCDProgram_ReplaceElemWithProgram failed", file_path); + NCDProgram_Free(&included_program); + goto fail2; + } + } + + elem = next_elem; + } + + free(dir_path); + + *out_program = program; + *out_guarded = 0; + return 1; + +fail2: + free_guards(our_guards); + NCDProgram_Free(&program); +fail1: + free(dir_path); +fail0: + return ret_val; +} + +int NCDBuildProgram_Build (const char *file_path, NCDProgram *out_program) +{ + ASSERT(file_path) + ASSERT(out_program) + + struct build_state st; + st.top_guard = NULL; + + int guarded; + int res = process_file(&st, 0, file_path, out_program, &guarded); + + ASSERT(!res || !guarded) + + free_guards(st.top_guard); + + return res; +} diff --git a/external/badvpn_dns/ncd/NCDBuildProgram.h b/external/badvpn_dns/ncd/NCDBuildProgram.h new file mode 100644 index 00000000..dff66856 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDBuildProgram.h @@ -0,0 +1,49 @@ +/** + * @file NCDBuildProgram.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BUILD_PROGRAM_H +#define NCD_BUILD_PROGRAM_H + +#include +#include + +/** + * Builds an NCD program in AST form suitable for passing to {@link NCDInterpreter}, + * by opening and parsing it, as well as recursively processing any included files. + * The resulting program will not contain any 'include' or 'include_guard' elements; + * these will be resolved and removed. + * + * @param file_path path to the main file of the program + * @param out_program on success, *out_program will contain the resulting program. + * On failure, *out_program will be unchanged. + * @return 1 on success, 0 on failure + */ +int NCDBuildProgram_Build (const char *file_path, NCDProgram *out_program) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDConfigParser.c b/external/badvpn_dns/ncd/NCDConfigParser.c new file mode 100644 index 00000000..f883f728 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDConfigParser.c @@ -0,0 +1,214 @@ +/** + * @file NCDConfigParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "ncd/NCDConfigParser.h" + +#include + +#include "../generated/NCDConfigParser_parse.c" +#include "../generated/NCDConfigParser_parse.h" + +struct parser_state { + struct parser_out out; + int error; + void *parser; +}; + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + struct parser_state *state = user; + ASSERT(!state->out.out_of_memory) + ASSERT(!state->out.syntax_error) + ASSERT(!state->error) + + if (token == NCD_ERROR) { + BLog(BLOG_ERROR, "line %zu, character %zu: tokenizer error", line, line_char); + state->error = 1; + return 0; + } + + struct token minor; + minor.str = value; + minor.len = value_len; + + switch (token) { + case NCD_EOF: { + Parse(state->parser, 0, minor, &state->out); + } break; + + case NCD_TOKEN_CURLY_OPEN: { + Parse(state->parser, CURLY_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_CURLY_CLOSE: { + Parse(state->parser, CURLY_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_ROUND_OPEN: { + Parse(state->parser, ROUND_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_ROUND_CLOSE: { + Parse(state->parser, ROUND_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_SEMICOLON: { + Parse(state->parser, SEMICOLON, minor, &state->out); + } break; + + case NCD_TOKEN_DOT: { + Parse(state->parser, DOT, minor, &state->out); + } break; + + case NCD_TOKEN_COMMA: { + Parse(state->parser, COMMA, minor, &state->out); + } break; + + case NCD_TOKEN_ARROW: { + Parse(state->parser, ARROW, minor, &state->out); + } break; + + case NCD_TOKEN_PROCESS: { + Parse(state->parser, PROCESS, minor, &state->out); + } break; + + case NCD_TOKEN_TEMPLATE: { + Parse(state->parser, TEMPLATE, minor, &state->out); + } break; + + case NCD_TOKEN_NAME: { + Parse(state->parser, NAME, minor, &state->out); + } break; + + case NCD_TOKEN_STRING: { + Parse(state->parser, STRING, minor, &state->out); + } break; + + case NCD_TOKEN_COLON: { + Parse(state->parser, COLON, minor, &state->out); + } break; + + case NCD_TOKEN_BRACKET_OPEN: { + Parse(state->parser, BRACKET_OPEN, minor, &state->out); + } break; + + case NCD_TOKEN_BRACKET_CLOSE: { + Parse(state->parser, BRACKET_CLOSE, minor, &state->out); + } break; + + case NCD_TOKEN_IF: { + Parse(state->parser, IF, minor, &state->out); + } break; + + case NCD_TOKEN_ELIF: { + Parse(state->parser, ELIF, minor, &state->out); + } break; + + case NCD_TOKEN_ELSE: { + Parse(state->parser, ELSE, minor, &state->out); + } break; + + case NCD_TOKEN_FOREACH: { + Parse(state->parser, FOREACH, minor, &state->out); + } break; + + case NCD_TOKEN_AS: { + Parse(state->parser, AS, minor, &state->out); + } break; + + case NCD_TOKEN_INCLUDE: { + Parse(state->parser, INCLUDE, minor, &state->out); + } break; + + case NCD_TOKEN_INCLUDE_GUARD: { + Parse(state->parser, INCLUDE_GUARD, minor, &state->out); + } break; + + default: + BLog(BLOG_ERROR, "line %zu, character %zu: invalid token", line, line_char); + free(minor.str); + state->error = 1; + return 0; + } + + // if we got syntax error, stop parsing + if (state->out.syntax_error) { + BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char); + state->error = 1; + return 0; + } + + if (state->out.out_of_memory) { + BLog(BLOG_ERROR, "line %zu, character %zu: out of memory", line, line_char); + state->error = 1; + return 0; + } + + return 1; +} + +int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast) +{ + struct parser_state state; + + state.out.out_of_memory = 0; + state.out.syntax_error = 0; + state.out.have_ast = 0; + state.error = 0; + + if (!(state.parser = ParseAlloc(malloc))) { + BLog(BLOG_ERROR, "ParseAlloc failed"); + return 0; + } + + // tokenize and parse + NCDConfigTokenizer_Tokenize(config, config_len, tokenizer_output, &state); + + ParseFree(state.parser, free); + + if (state.error) { + if (state.out.have_ast) { + NCDProgram_Free(&state.out.ast); + } + return 0; + } + + ASSERT(state.out.have_ast) + + *out_ast = state.out.ast; + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDConfigParser.h b/external/badvpn_dns/ncd/NCDConfigParser.h new file mode 100644 index 00000000..90cee54c --- /dev/null +++ b/external/badvpn_dns/ncd/NCDConfigParser.h @@ -0,0 +1,40 @@ +/** + * @file NCDConfigParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDCONFIG_NCDCONFIGPARSER_H +#define BADVPN_NCDCONFIG_NCDCONFIGPARSER_H + +#include + +#include +#include + +int NCDConfigParser_Parse (char *config, size_t config_len, NCDProgram *out_ast) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDConfigParser_parse.y b/external/badvpn_dns/ncd/NCDConfigParser_parse.y new file mode 100644 index 00000000..fdf89f6d --- /dev/null +++ b/external/badvpn_dns/ncd/NCDConfigParser_parse.y @@ -0,0 +1,718 @@ +/** + * @file NCDConfigParser.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%include { + +#include +#include + +#include +#include +#include + +struct parser_out { + int out_of_memory; + int syntax_error; + int have_ast; + NCDProgram ast; +}; + +struct token { + char *str; + size_t len; +}; + +struct program { + int have; + NCDProgram v; +}; + +struct block { + int have; + NCDBlock v; +}; + +struct statement { + int have; + NCDStatement v; +}; + +struct ifblock { + int have; + NCDIfBlock v; +}; + +struct value { + int have; + NCDValue v; +}; + +static void free_token (struct token o) { free(o.str); } +static void free_program (struct program o) { if (o.have) NCDProgram_Free(&o.v); } +static void free_block (struct block o) { if (o.have) NCDBlock_Free(&o.v); } +static void free_statement (struct statement o) { if (o.have) NCDStatement_Free(&o.v); } +static void free_ifblock (struct ifblock o) { if (o.have) NCDIfBlock_Free(&o.v); } +static void free_value (struct value o) { if (o.have) NCDValue_Free(&o.v); } + +} + +%extra_argument { struct parser_out *parser_out } + +%token_type { struct token } + +%token_destructor { free_token($$); } + +%type processes { struct program } +%type statement { struct statement } +%type elif_maybe { struct ifblock } +%type elif { struct ifblock } +%type else_maybe { struct block } +%type statements { struct block } +%type dotted_name { char * } +%type statement_args_maybe { struct value } +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } +%type name_maybe { char * } +%type process_or_template { int } + +// mention parser_out in some destructor to a void unused variable warning +%destructor processes { (void)parser_out; free_program($$); } +%destructor statement { free_statement($$); } +%destructor elif_maybe { free_ifblock($$); } +%destructor elif { free_ifblock($$); } +%destructor else_maybe { free_block($$); } +%destructor statements { free_block($$); } +%destructor dotted_name { free($$); } +%destructor statement_args_maybe { free_value($$); } +%destructor list_contents { free_value($$); } +%destructor list { free_value($$); } +%destructor map_contents { free_value($$); } +%destructor map { free_value($$); } +%destructor value { free_value($$); } +%destructor name_maybe { free($$); } + +%stack_size 0 + +%syntax_error { + parser_out->syntax_error = 1; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= processes(A). { + ASSERT(!parser_out->have_ast) + + if (A.have) { + parser_out->have_ast = 1; + parser_out->ast = A.v; + } +} + +processes(R) ::= . { + NCDProgram prog; + NCDProgram_Init(&prog); + + R.have = 1; + R.v = prog; +} + +processes(R) ::= INCLUDE STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failA0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitInclude(&elem, A.str, A.len)) { + goto failA0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failA1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneA; + +failA1: + NCDProgramElem_Free(&elem); +failA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneA: + free_token(A); + free_program(N); +} + +processes(R) ::= INCLUDE_GUARD STRING(A) processes(N). { + ASSERT(A.str) + if (!N.have) { + goto failZ0; + } + + NCDProgramElem elem; + if (!NCDProgramElem_InitIncludeGuard(&elem, A.str, A.len)) { + goto failZ0; + } + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failZ1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneZ; + +failZ1: + NCDProgramElem_Free(&elem); +failZ0: + R.have = 0; + parser_out->out_of_memory = 1; +doneZ: + free_token(A); + free_program(N); +} + +processes(R) ::= process_or_template(T) NAME(A) CURLY_OPEN statements(B) CURLY_CLOSE processes(N). { + ASSERT(A.str) + if (!B.have || !N.have) { + goto failB0; + } + + NCDProcess proc; + if (!NCDProcess_Init(&proc, T, A.str, B.v)) { + goto failB0; + } + B.have = 0; + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc); + + if (!NCDProgram_PrependElem(&N.v, elem)) { + goto failB1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneB; + +failB1: + NCDProgramElem_Free(&elem); +failB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneB: + free_token(A); + free_block(B); + free_program(N); +} + +statement(R) ::= dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!A || !B.have) { + goto failC0; + } + + if (!NCDStatement_InitReg(&R.v, C, NULL, A, B.v)) { + goto failC0; + } + B.have = 0; + + R.have = 1; + goto doneC; + +failC0: + R.have = 0; + parser_out->out_of_memory = 1; +doneC: + free(A); + free_value(B); + free(C); +} + +statement(R) ::= dotted_name(M) ARROW dotted_name(A) ROUND_OPEN statement_args_maybe(B) ROUND_CLOSE name_maybe(C) SEMICOLON. { + if (!M || !A || !B.have) { + goto failD0; + } + + if (!NCDStatement_InitReg(&R.v, C, M, A, B.v)) { + goto failD0; + } + B.have = 0; + + R.have = 1; + goto doneD; + +failD0: + R.have = 0; + parser_out->out_of_memory = 1; +doneD: + free(M); + free(A); + free_value(B); + free(C); +} + +statement(R) ::= IF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif_maybe(I) else_maybe(E) name_maybe(C) SEMICOLON. { + if (!A.have || !B.have || !I.have) { + goto failE0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&I.v, ifc)) { + NCDIf_Free(&ifc); + goto failE0; + } + + if (!NCDStatement_InitIf(&R.v, C, I.v)) { + goto failE0; + } + I.have = 0; + + if (E.have) { + NCDStatement_IfAddElse(&R.v, E.v); + E.have = 0; + } + + R.have = 1; + goto doneE; + +failE0: + R.have = 0; + parser_out->out_of_memory = 1; +doneE: + free_value(A); + free_block(B); + free_ifblock(I); + free_block(E); + free(C); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !S.have) { + goto failEA0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, NULL, S.v)) { + goto failEA0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEA0; + +failEA0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEA0: + free_value(A); + free_token(B); + free_block(S); + free(N); +} + +statement(R) ::= FOREACH ROUND_OPEN value(A) AS NAME(B) COLON NAME(C) ROUND_CLOSE CURLY_OPEN statements(S) CURLY_CLOSE name_maybe(N) SEMICOLON. { + if (!A.have || !B.str || !C.str || !S.have) { + goto failEB0; + } + + if (!NCDStatement_InitForeach(&R.v, N, A.v, B.str, C.str, S.v)) { + goto failEB0; + } + A.have = 0; + S.have = 0; + + R.have = 1; + goto doneEB0; + +failEB0: + R.have = 0; + parser_out->out_of_memory = 1; +doneEB0: + free_value(A); + free_token(B); + free_token(C); + free_block(S); + free(N); +} + +elif_maybe(R) ::= . { + NCDIfBlock_Init(&R.v); + R.have = 1; +} + +elif_maybe(R) ::= elif(A). { + R = A; +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE. { + if (!A.have || !B.have) { + goto failF0; + } + + NCDIfBlock_Init(&R.v); + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&R.v, ifc)) { + goto failF1; + } + + R.have = 1; + goto doneF0; + +failF1: + NCDIf_Free(&ifc); + NCDIfBlock_Free(&R.v); +failF0: + R.have = 0; + parser_out->out_of_memory = 1; +doneF0: + free_value(A); + free_block(B); +} + +elif(R) ::= ELIF ROUND_OPEN value(A) ROUND_CLOSE CURLY_OPEN statements(B) CURLY_CLOSE elif(N). { + if (!A.have || !B.have || !N.have) { + goto failG0; + } + + NCDIf ifc; + NCDIf_Init(&ifc, A.v, B.v); + A.have = 0; + B.have = 0; + + if (!NCDIfBlock_PrependIf(&N.v, ifc)) { + goto failG1; + } + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneG0; + +failG1: + NCDIf_Free(&ifc); +failG0: + R.have = 0; + parser_out->out_of_memory = 1; +doneG0: + free_value(A); + free_block(B); + free_ifblock(N); +} + +else_maybe(R) ::= . { + R.have = 0; +} + +else_maybe(R) ::= ELSE CURLY_OPEN statements(B) CURLY_CLOSE. { + R = B; +} + +statements(R) ::= statement(A). { + if (!A.have) { + goto failH0; + } + + NCDBlock_Init(&R.v); + + if (!NCDBlock_PrependStatement(&R.v, A.v)) { + goto failH1; + } + A.have = 0; + + R.have = 1; + goto doneH; + +failH1: + NCDBlock_Free(&R.v); +failH0: + R.have = 0; + parser_out->out_of_memory = 1; +doneH: + free_statement(A); +} + +statements(R) ::= statement(A) statements(N). { + if (!A.have || !N.have) { + goto failI0; + } + + if (!NCDBlock_PrependStatement(&N.v, A.v)) { + goto failI1; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneI; + +failI1: + NCDBlock_Free(&R.v); +failI0: + R.have = 0; + parser_out->out_of_memory = 1; +doneI: + free_statement(A); + free_block(N); +} + +dotted_name(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +dotted_name(R) ::= NAME(A) DOT dotted_name(N). { + ASSERT(A.str) + if (!N) { + goto failJ0; + } + + if (!(R = concat_strings(3, A.str, ".", N))) { + goto failJ0; + } + + goto doneJ; + +failJ0: + R = NULL; + parser_out->out_of_memory = 1; +doneJ: + free_token(A); + free(N); +} + +statement_args_maybe(R) ::= . { + R.have = 1; + NCDValue_InitList(&R.v); +} + +statement_args_maybe(R) ::= list_contents(A). { + R = A; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValue_InitList(&R.v); + + if (!NCDValue_ListPrepend(&R.v, A.v)) { + goto failL1; + } + A.have = 0; + + R.have = 1; + goto doneL; + +failL1: + NCDValue_Free(&R.v); +failL0: + R.have = 0; + parser_out->out_of_memory = 1; +doneL: + free_value(A); +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValue_ListPrepend(&N.v, A.v)) { + goto failM0; + } + A.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneM; + +failM0: + R.have = 0; + parser_out->out_of_memory = 1; +doneM: + free_value(A); + free_value(N); +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + R.have = 1; + NCDValue_InitList(&R.v); +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValue_InitMap(&R.v); + + if (!NCDValue_MapPrepend(&R.v, A.v, B.v)) { + goto failS1; + } + A.have = 0; + B.have = 0; + + R.have = 1; + goto doneS; + +failS1: + NCDValue_Free(&R.v); +failS0: + R.have = 0; + parser_out->out_of_memory = 1; +doneS: + free_value(A); + free_value(B); +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValue_MapPrepend(&N.v, A.v, B.v)) { + goto failT0; + } + A.have = 0; + B.have = 0; + + R.have = 1; + R.v = N.v; + N.have = 0; + goto doneT; + +failT0: + R.have = 0; + parser_out->out_of_memory = 1; +doneT: + free_value(A); + free_value(B); + free_value(N); +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + R.have = 1; + NCDValue_InitMap(&R.v); +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValue_InitStringBin(&R.v, (uint8_t *)A.str, A.len)) { + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; + parser_out->out_of_memory = 1; +doneU: + free_token(A); +} + +value(R) ::= dotted_name(A). { + if (!A) { + goto failV0; + } + + if (!NCDValue_InitVar(&R.v, A)) { + goto failV0; + } + + R.have = 1; + goto doneV; + +failV0: + R.have = 0; + parser_out->out_of_memory = 1; +doneV: + free(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} + +name_maybe(R) ::= . { + R = NULL; +} + +name_maybe(R) ::= NAME(A). { + ASSERT(A.str) + + R = A.str; +} + +process_or_template(R) ::= PROCESS. { + R = 0; +} + +process_or_template(R) ::= TEMPLATE. { + R = 1; +} diff --git a/external/badvpn_dns/ncd/NCDConfigTokenizer.c b/external/badvpn_dns/ncd/NCDConfigTokenizer.c new file mode 100644 index 00000000..5ba31b49 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDConfigTokenizer.c @@ -0,0 +1,321 @@ +/** + * @file NCDConfigTokenizer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int is_name_char (char c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'); +} + +static int is_name_first_char (char c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'); +} + +static int is_space_char (char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static int string_equals (char *str, int str_len, char *needle) +{ + return (str_len == strlen(needle) && !memcmp(str, needle, str_len)); +} + +void NCDConfigTokenizer_Tokenize (char *str, size_t left, NCDConfigTokenizer_output output, void *user) +{ + size_t line = 1; + size_t line_char = 1; + + while (left > 0) { + size_t l; + int error = 0; + int token; + void *token_val = NULL; + size_t token_len = 0; + + if (*str == '#') { + l = 1; + while (l < left && str[l] != '\n') { + l++; + } + token = 0; + } + else if (l = data_begins_with(str, left, "{")) { + token = NCD_TOKEN_CURLY_OPEN; + } + else if (l = data_begins_with(str, left, "}")) { + token = NCD_TOKEN_CURLY_CLOSE; + } + else if (l = data_begins_with(str, left, "(")) { + token = NCD_TOKEN_ROUND_OPEN; + } + else if (l = data_begins_with(str, left, ")")) { + token = NCD_TOKEN_ROUND_CLOSE; + } + else if (l = data_begins_with(str, left, ";")) { + token = NCD_TOKEN_SEMICOLON; + } + else if (l = data_begins_with(str, left, ".")) { + token = NCD_TOKEN_DOT; + } + else if (l = data_begins_with(str, left, ",")) { + token = NCD_TOKEN_COMMA; + } + else if (l = data_begins_with(str, left, ":")) { + token = NCD_TOKEN_COLON; + } + else if (l = data_begins_with(str, left, "[")) { + token = NCD_TOKEN_BRACKET_OPEN; + } + else if (l = data_begins_with(str, left, "]")) { + token = NCD_TOKEN_BRACKET_CLOSE; + } + else if (l = data_begins_with(str, left, "->")) { + token = NCD_TOKEN_ARROW; + } + else if (l = data_begins_with(str, left, "If")) { + token = NCD_TOKEN_IF; + } + else if (l = data_begins_with(str, left, "Elif")) { + token = NCD_TOKEN_ELIF; + } + else if (l = data_begins_with(str, left, "elif")) { + token = NCD_TOKEN_ELIF; + } + else if (l = data_begins_with(str, left, "Else")) { + token = NCD_TOKEN_ELSE; + } + else if (l = data_begins_with(str, left, "else")) { + token = NCD_TOKEN_ELSE; + } + else if (l = data_begins_with(str, left, "Foreach")) { + token = NCD_TOKEN_FOREACH; + } + else if (l = data_begins_with(str, left, "As")) { + token = NCD_TOKEN_AS; + } + else if (l = data_begins_with(str, left, "include_guard")) { + token = NCD_TOKEN_INCLUDE_GUARD; + } + else if (l = data_begins_with(str, left, "include")) { + token = NCD_TOKEN_INCLUDE; + } + else if (is_name_first_char(*str)) { + l = 1; + while (l < left && is_name_char(str[l])) { + l++; + } + + // allocate buffer + bsize_t bufsize = bsize_add(bsize_fromsize(l), bsize_fromint(1)); + char *buf; + if (bufsize.is_overflow || !(buf = malloc(bufsize.value))) { + BLog(BLOG_ERROR, "malloc failed"); + error = 1; + goto out; + } + + // copy and terminate + memcpy(buf, str, l); + buf[l] = '\0'; + + if (!strcmp(buf, "process")) { + token = NCD_TOKEN_PROCESS; + free(buf); + } + else if (!strcmp(buf, "template")) { + token = NCD_TOKEN_TEMPLATE; + free(buf); + } + else { + token = NCD_TOKEN_NAME; + token_val = buf; + token_len = l; + } + } + else if (*str == '"') do { + // init string + ExpString estr; + if (!ExpString_Init(&estr)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto string_fail0; + } + + // skip start quote + l = 1; + + // decode string + while (l < left) { + uint8_t dec_ch; + + // get character + if (str[l] == '\\') { + if (left - l < 2) { + BLog(BLOG_ERROR, "escape character found in string but nothing follows"); + goto string_fail1; + } + + size_t extra = 0; + + switch (str[l + 1]) { + case '\'': + case '\"': + case '\\': + case '\?': + dec_ch = str[l + 1]; break; + + case 'a': + dec_ch = '\a'; break; + case 'b': + dec_ch = '\b'; break; + case 'f': + dec_ch = '\f'; break; + case 'n': + dec_ch = '\n'; break; + case 'r': + dec_ch = '\r'; break; + case 't': + dec_ch = '\t'; break; + case 'v': + dec_ch = '\v'; break; + + case '0': + dec_ch = 0; break; + + case 'x': { + if (left - l < 4) { + BLog(BLOG_ERROR, "hexadecimal escape found in string but too little characters follow"); + goto string_fail1; + } + + uintmax_t hex_val; + if (!parse_unsigned_hex_integer_bin(&str[l + 2], 2, &hex_val)) { + BLog(BLOG_ERROR, "hexadecimal escape found in string but two hex characters don't follow"); + goto string_fail1; + } + + dec_ch = hex_val; + extra = 2; + } break; + + default: + BLog(BLOG_ERROR, "bad escape sequence in string"); + goto string_fail1; + } + + l += 2 + extra; + } + else if (str[l] == '"') { + break; + } + else { + dec_ch = str[l]; + l++; + } + + // append character to string + if (!ExpString_AppendByte(&estr, dec_ch)) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto string_fail1; + } + } + + // make sure ending quote was found + if (l == left) { + BLog(BLOG_ERROR, "missing ending quote for string"); + goto string_fail1; + } + + // skip ending quote + l++; + + token = NCD_TOKEN_STRING; + token_val = ExpString_Get(&estr); + token_len = ExpString_Length(&estr); + break; + + string_fail1: + ExpString_Free(&estr); + string_fail0: + error = 1; + } while (0); + else if (is_space_char(*str)) { + token = 0; + l = 1; + } + else { + BLog(BLOG_ERROR, "unrecognized character"); + error = 1; + } + + out: + // report error + if (error) { + output(user, NCD_ERROR, NULL, 0, line, line_char); + return; + } + + // output token + if (token) { + if (!output(user, token, token_val, token_len, line, line_char)) { + return; + } + } + + // update line/char counters + for (size_t i = 0; i < l; i++) { + if (str[i] == '\n') { + line++; + line_char = 1; + } else { + line_char++; + } + } + + str += l; + left -= l; + } + + output(user, NCD_EOF, NULL, 0, line, line_char); +} diff --git a/external/badvpn_dns/ncd/NCDConfigTokenizer.h b/external/badvpn_dns/ncd/NCDConfigTokenizer.h new file mode 100644 index 00000000..38edfade --- /dev/null +++ b/external/badvpn_dns/ncd/NCDConfigTokenizer.h @@ -0,0 +1,64 @@ +/** + * @file NCDConfigTokenizer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H +#define BADVPN_NCDCONFIG_NCDCONFIGTOKENIZER_H + +#include + +#define NCD_ERROR -1 +#define NCD_EOF 0 +#define NCD_TOKEN_CURLY_OPEN 1 +#define NCD_TOKEN_CURLY_CLOSE 2 +#define NCD_TOKEN_ROUND_OPEN 3 +#define NCD_TOKEN_ROUND_CLOSE 4 +#define NCD_TOKEN_SEMICOLON 5 +#define NCD_TOKEN_DOT 6 +#define NCD_TOKEN_COMMA 7 +#define NCD_TOKEN_PROCESS 8 +#define NCD_TOKEN_NAME 9 +#define NCD_TOKEN_STRING 10 +#define NCD_TOKEN_ARROW 11 +#define NCD_TOKEN_TEMPLATE 12 +#define NCD_TOKEN_COLON 13 +#define NCD_TOKEN_BRACKET_OPEN 14 +#define NCD_TOKEN_BRACKET_CLOSE 15 +#define NCD_TOKEN_IF 16 +#define NCD_TOKEN_ELIF 17 +#define NCD_TOKEN_ELSE 18 +#define NCD_TOKEN_FOREACH 19 +#define NCD_TOKEN_AS 20 +#define NCD_TOKEN_INCLUDE 21 +#define NCD_TOKEN_INCLUDE_GUARD 22 + +typedef int (*NCDConfigTokenizer_output) (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char); + +void NCDConfigTokenizer_Tokenize (char *str, size_t str_len, NCDConfigTokenizer_output output, void *user); + +#endif diff --git a/external/badvpn_dns/ncd/NCDInterpProcess.c b/external/badvpn_dns/ncd/NCDInterpProcess.c new file mode 100644 index 00000000..4462ea62 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpProcess.c @@ -0,0 +1,497 @@ +/** + * @file NCDInterpProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "NCDInterpProcess.h" + +#include + +static int compute_prealloc (NCDInterpProcess *o) +{ + int size = 0; + + for (int i = 0; i < o->num_stmts; i++) { + int mod = size % BMAX_ALIGN; + int align_size = (mod == 0 ? 0 : BMAX_ALIGN - mod); + + if (align_size + o->stmts[i].alloc_size > INT_MAX - size) { + return 0; + } + + o->stmts[i].prealloc_offset = size + align_size; + size += align_size + o->stmts[i].alloc_size; + } + + ASSERT(size >= 0) + + o->prealloc_size = size; + + return 1; +} + +static int convert_value_recurser (NCDPlaceholderDb *pdb, NCDStringIndex *string_index, NCDValue *value, NCDValMem *mem, NCDValRef *out) +{ + ASSERT(pdb) + ASSERT(string_index) + ASSERT((NCDValue_Type(value), 1)) + ASSERT(mem) + ASSERT(out) + + switch (NCDValue_Type(value)) { + case NCDVALUE_STRING: { + const char *str = NCDValue_StringValue(value); + size_t len = NCDValue_StringLength(value); + + NCD_string_id_t string_id = NCDStringIndex_GetBin(string_index, str, len); + if (string_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed"); + goto fail; + } + + *out = NCDVal_NewIdString(mem, string_id, string_index); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + } break; + + case NCDVALUE_LIST: { + *out = NCDVal_NewList(mem, NCDValue_ListCount(value)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (NCDValue *e = NCDValue_ListFirst(value); e; e = NCDValue_ListNext(value, e)) { + NCDValRef vval; + if (!convert_value_recurser(pdb, string_index, e, mem, &vval)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, vval)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + } + } break; + + case NCDVALUE_MAP: { + *out = NCDVal_NewMap(mem, NCDValue_MapCount(value)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (NCDValue *ekey = NCDValue_MapFirstKey(value); ekey; ekey = NCDValue_MapNextKey(value, ekey)) { + NCDValue *eval = NCDValue_MapKeyValue(value, ekey); + + NCDValRef vkey; + NCDValRef vval; + if (!convert_value_recurser(pdb, string_index, ekey, mem, &vkey) || + !convert_value_recurser(pdb, string_index, eval, mem, &vval) + ) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(*out, vkey, vval, &inserted)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + if (!inserted) { + BLog(BLOG_ERROR, "duplicate key in map"); + goto fail; + } + } + } break; + + case NCDVALUE_VAR: { + int plid; + if (!NCDPlaceholderDb_AddVariable(pdb, NCDValue_VarName(value), &plid)) { + goto fail; + } + + if (NCDVAL_MINIDX + plid >= -1) { + goto fail; + } + + *out = NCDVal_NewPlaceholder(mem, plid); + } break; + + default: + goto fail; + } + + return 1; + +fail: + return 0; +} + +int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) +{ + ASSERT(process) + ASSERT(string_index) + ASSERT(pdb) + ASSERT(module_index) + + NCDBlock *block = NCDProcess_Block(process); + + if (NCDBlock_NumStatements(block) > INT_MAX) { + BLog(BLOG_ERROR, "too many statements"); + goto fail0; + } + int num_stmts = NCDBlock_NumStatements(block); + + if (!(o->stmts = BAllocArray(num_stmts, sizeof(o->stmts[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + o->num_hash_buckets = num_stmts; + + if (!(o->hash_buckets = BAllocArray(o->num_hash_buckets, sizeof(o->hash_buckets[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + for (size_t i = 0; i < o->num_hash_buckets; i++) { + o->hash_buckets[i] = -1; + } + + if (!(o->name = b_strdup(NCDProcess_Name(process)))) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail2; + } + + o->num_stmts = 0; + o->prealloc_size = -1; + o->is_template = NCDProcess_IsTemplate(process); + o->cache = NULL; + + for (NCDStatement *s = NCDBlock_FirstStatement(block); s; s = NCDBlock_NextStatement(block, s)) { + ASSERT(NCDStatement_Type(s) == NCDSTATEMENT_REG) + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + + e->name = -1; + e->objnames = NULL; + e->num_objnames = 0; + e->alloc_size = 0; + + if (NCDStatement_Name(s)) { + e->name = NCDStringIndex_Get(string_index, NCDStatement_Name(s)); + if (e->name < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + } + + e->cmdname = NCDStringIndex_Get(string_index, NCDStatement_RegCmdName(s)); + if (e->cmdname < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + + NCDValMem_Init(&e->arg_mem); + + NCDValRef val; + if (!convert_value_recurser(pdb, string_index, NCDStatement_RegArgs(s), &e->arg_mem, &val)) { + BLog(BLOG_ERROR, "convert_value_recurser failed"); + goto loop_fail1; + } + + e->arg_ref = NCDVal_ToSafe(val); + + if (!NCDValReplaceProg_Init(&e->arg_prog, val)) { + BLog(BLOG_ERROR, "NCDValReplaceProg_Init failed"); + goto loop_fail1; + } + + if (NCDStatement_RegObjName(s)) { + if (!ncd_make_name_indices(string_index, NCDStatement_RegObjName(s), &e->objnames, &e->num_objnames)) { + BLog(BLOG_ERROR, "ncd_make_name_indices failed"); + goto loop_fail2; + } + + e->binding.method_name_id = NCDModuleIndex_GetMethodNameId(module_index, NCDStatement_RegCmdName(s)); + if (e->binding.method_name_id == -1) { + BLog(BLOG_ERROR, "NCDModuleIndex_GetMethodNameId failed"); + goto loop_fail3; + } + } else { + e->binding.simple_module = NCDModuleIndex_FindModule(module_index, NCDStatement_RegCmdName(s)); + } + + if (e->name >= 0) { + size_t bucket_idx = e->name % o->num_hash_buckets; + e->hash_next = o->hash_buckets[bucket_idx]; + o->hash_buckets[bucket_idx] = o->num_stmts; + } + + o->num_stmts++; + continue; + + loop_fail3: + BFree(e->objnames); + loop_fail2: + NCDValReplaceProg_Free(&e->arg_prog); + loop_fail1: + NCDValMem_Free(&e->arg_mem); + loop_fail0: + goto fail3; + } + + ASSERT(o->num_stmts == num_stmts) + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + while (o->num_stmts-- > 0) { + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + BFree(e->objnames); + NCDValReplaceProg_Free(&e->arg_prog); + NCDValMem_Free(&e->arg_mem); + } + free(o->name); +fail2: + BFree(o->hash_buckets); +fail1: + BFree(o->stmts); +fail0: + return 0; +} + +void NCDInterpProcess_Free (NCDInterpProcess *o) +{ + DebugObject_Free(&o->d_obj); + + while (o->num_stmts-- > 0) { + struct NCDInterpProcess__stmt *e = &o->stmts[o->num_stmts]; + BFree(e->objnames); + NCDValReplaceProg_Free(&e->arg_prog); + NCDValMem_Free(&e->arg_mem); + } + + free(o->name); + BFree(o->hash_buckets); + BFree(o->stmts); +} + +int NCDInterpProcess_FindStatement (NCDInterpProcess *o, int from_index, NCD_string_id_t name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(from_index >= 0) + ASSERT(from_index <= o->num_stmts) + + size_t bucket_idx = name % o->num_hash_buckets; + int stmt_idx = o->hash_buckets[bucket_idx]; + ASSERT(stmt_idx >= -1) + ASSERT(stmt_idx < o->num_stmts) + + while (stmt_idx >= 0) { + if (stmt_idx < from_index && o->stmts[stmt_idx].name == name) { + return stmt_idx; + } + + stmt_idx = o->stmts[stmt_idx].hash_next; + ASSERT(stmt_idx >= -1) + ASSERT(stmt_idx < o->num_stmts) + } + + return -1; +} + +const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i, NCDStringIndex *string_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(string_index) + + return NCDStringIndex_Value(string_index, o->stmts[i].cmdname); +} + +void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(out_objnames) + ASSERT(out_num_objnames) + + *out_objnames = o->stmts[i].objnames; + *out_num_objnames = o->stmts[i].num_objnames; +} + +const struct NCDInterpModule * NCDInterpProcess_StatementGetSimpleModule (NCDInterpProcess *o, int i, NCDStringIndex *string_index, NCDModuleIndex *module_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(!o->stmts[i].objnames) + + struct NCDInterpProcess__stmt *e = &o->stmts[i]; + + if (!e->binding.simple_module) { + const char *cmdname = NCDStringIndex_Value(string_index, e->cmdname); + e->binding.simple_module = NCDModuleIndex_FindModule(module_index, cmdname); + } + + return e->binding.simple_module; +} + +const struct NCDInterpModule * NCDInterpProcess_StatementGetMethodModule (NCDInterpProcess *o, int i, NCD_string_id_t obj_type, NCDModuleIndex *module_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->stmts[i].objnames) + ASSERT(obj_type >= 0) + ASSERT(module_index) + + return NCDModuleIndex_GetMethodModule(module_index, obj_type, o->stmts[i].binding.method_name_id); +} + +int NCDInterpProcess_CopyStatementArgs (NCDInterpProcess *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(out_valmem) + ASSERT(out_val) + ASSERT(out_prog) + + struct NCDInterpProcess__stmt *e = &o->stmts[i]; + + if (!NCDValMem_InitCopy(out_valmem, &e->arg_mem)) { + return 0; + } + + *out_val = NCDVal_FromSafe(out_valmem, e->arg_ref); + *out_prog = e->arg_prog; + return 1; +} + +void NCDInterpProcess_StatementBumpAllocSize (NCDInterpProcess *o, int i, int alloc_size) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(alloc_size >= 0) + + if (alloc_size > o->stmts[i].alloc_size) { + o->stmts[i].alloc_size = alloc_size; + o->prealloc_size = -1; + } +} + +int NCDInterpProcess_PreallocSize (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->prealloc_size == -1 || o->prealloc_size >= 0) + + if (o->prealloc_size < 0 && !compute_prealloc(o)) { + return -1; + } + + return o->prealloc_size; +} + +int NCDInterpProcess_StatementPreallocSize (NCDInterpProcess *o, int i) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->prealloc_size >= 0) + + return o->stmts[i].alloc_size; +} + +int NCDInterpProcess_StatementPreallocOffset (NCDInterpProcess *o, int i) +{ + DebugObject_Access(&o->d_obj); + ASSERT(i >= 0) + ASSERT(i < o->num_stmts) + ASSERT(o->prealloc_size >= 0) + + return o->stmts[i].prealloc_offset; +} + +const char * NCDInterpProcess_Name (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->name; +} + +int NCDInterpProcess_IsTemplate (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->is_template; +} + +int NCDInterpProcess_NumStatements (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + return o->num_stmts; +} + +int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem) +{ + DebugObject_Access(&o->d_obj); + ASSERT(elem) + + if (o->cache) { + return 0; + } + + o->cache = elem; + + return 1; +} + +void * NCDInterpProcess_CachePull (NCDInterpProcess *o) +{ + DebugObject_Access(&o->d_obj); + + void *elem = o->cache; + o->cache = NULL; + + return elem; +} diff --git a/external/badvpn_dns/ncd/NCDInterpProcess.h b/external/badvpn_dns/ncd/NCDInterpProcess.h new file mode 100644 index 00000000..49fd2c67 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpProcess.h @@ -0,0 +1,100 @@ +/** + * @file NCDInterpProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDINTERPPROCESS_H +#define BADVPN_NCDINTERPPROCESS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct NCDInterpProcess__stmt { + NCD_string_id_t name; + NCD_string_id_t cmdname; + NCD_string_id_t *objnames; + size_t num_objnames; + union { + const struct NCDInterpModule *simple_module; + int method_name_id; + } binding; + NCDValMem arg_mem; + NCDValSafeRef arg_ref; + NCDValReplaceProg arg_prog; + int alloc_size; + int prealloc_offset; + int hash_next; +}; + +/** + * A data structure which contains information about a process or + * template, suitable for efficient interpretation. These structures + * are built at startup from the program AST for all processes and + * templates by \link NCDInterpProg. They are not modified after + * the program is loaded Inn case of template processes, the same + * NCDInterpProcess is shared by all processes created from the same + * template. + */ +typedef struct { + struct NCDInterpProcess__stmt *stmts; + char *name; + int num_stmts; + int prealloc_size; + int is_template; + int *hash_buckets; + size_t num_hash_buckets; + void *cache; + DebugObject d_obj; +} NCDInterpProcess; + +int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) WARN_UNUSED; +void NCDInterpProcess_Free (NCDInterpProcess *o); +int NCDInterpProcess_FindStatement (NCDInterpProcess *o, int from_index, NCD_string_id_t name); +const char * NCDInterpProcess_StatementCmdName (NCDInterpProcess *o, int i, NCDStringIndex *string_index); +void NCDInterpProcess_StatementObjNames (NCDInterpProcess *o, int i, const NCD_string_id_t **out_objnames, size_t *out_num_objnames); +const struct NCDInterpModule * NCDInterpProcess_StatementGetSimpleModule (NCDInterpProcess *o, int i, NCDStringIndex *string_index, NCDModuleIndex *module_index); +const struct NCDInterpModule * NCDInterpProcess_StatementGetMethodModule (NCDInterpProcess *o, int i, NCD_string_id_t obj_type, NCDModuleIndex *module_index); +int NCDInterpProcess_CopyStatementArgs (NCDInterpProcess *o, int i, NCDValMem *out_valmem, NCDValRef *out_val, NCDValReplaceProg *out_prog) WARN_UNUSED; +void NCDInterpProcess_StatementBumpAllocSize (NCDInterpProcess *o, int i, int alloc_size); +int NCDInterpProcess_PreallocSize (NCDInterpProcess *o); +int NCDInterpProcess_StatementPreallocSize (NCDInterpProcess *o, int i); +int NCDInterpProcess_StatementPreallocOffset (NCDInterpProcess *o, int i); +const char * NCDInterpProcess_Name (NCDInterpProcess *o); +int NCDInterpProcess_IsTemplate (NCDInterpProcess *o); +int NCDInterpProcess_NumStatements (NCDInterpProcess *o); +int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem) WARN_UNUSED; +void * NCDInterpProcess_CachePull (NCDInterpProcess *o); + +#endif diff --git a/external/badvpn_dns/ncd/NCDInterpProg.c b/external/badvpn_dns/ncd/NCDInterpProg.c new file mode 100644 index 00000000..7ee75dec --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpProg.c @@ -0,0 +1,140 @@ +/** + * @file NCDInterpProg.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "NCDInterpProg.h" + +#include + +#include "NCDInterpProg_hash.h" +#include + +int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) +{ + ASSERT(prog) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE)) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE_GUARD)) + ASSERT(string_index) + ASSERT(pdb) + ASSERT(module_index) + + if (NCDProgram_NumElems(prog) > INT_MAX) { + BLog(BLOG_ERROR, "too many processes"); + goto fail0; + } + int num_procs = NCDProgram_NumElems(prog); + + if (!(o->procs = BAllocArray(num_procs, sizeof(o->procs[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + if (!NCDInterpProg__Hash_Init(&o->hash, num_procs)) { + BLog(BLOG_ERROR, "NCDInterpProg__Hash_Init failed"); + goto fail1; + } + + o->num_procs = 0; + + for (NCDProgramElem *elem = NCDProgram_FirstElem(prog); elem; elem = NCDProgram_NextElem(prog, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *p = NCDProgramElem_Process(elem); + + struct NCDInterpProg__process *e = &o->procs[o->num_procs]; + + e->name = NCDStringIndex_Get(string_index, NCDProcess_Name(p)); + if (e->name < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail2; + } + + if (!NCDInterpProcess_Init(&e->iprocess, p, string_index, pdb, module_index)) { + BLog(BLOG_ERROR, "NCDInterpProcess_Init failed"); + goto fail2; + } + + NCDInterpProg__HashRef ref = {e, o->num_procs}; + if (!NCDInterpProg__Hash_Insert(&o->hash, o->procs, ref, NULL)) { + BLog(BLOG_ERROR, "duplicate process or template name: %s", NCDProcess_Name(p)); + NCDInterpProcess_Free(&e->iprocess); + goto fail2; + } + + o->num_procs++; + } + + ASSERT(o->num_procs == num_procs) + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + while (o->num_procs-- > 0) { + NCDInterpProcess_Free(&o->procs[o->num_procs].iprocess); + } + NCDInterpProg__Hash_Free(&o->hash); +fail1: + BFree(o->procs); +fail0: + return 0; +} + +void NCDInterpProg_Free (NCDInterpProg *o) +{ + DebugObject_Free(&o->d_obj); + + while (o->num_procs-- > 0) { + NCDInterpProcess_Free(&o->procs[o->num_procs].iprocess); + } + + NCDInterpProg__Hash_Free(&o->hash); + + BFree(o->procs); +} + +NCDInterpProcess * NCDInterpProg_FindProcess (NCDInterpProg *o, NCD_string_id_t name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(name >= 0) + + NCDInterpProg__HashRef ref = NCDInterpProg__Hash_Lookup(&o->hash, o->procs, name); + if (ref.link == NCDInterpProg__HashNullLink()) { + return NULL; + } + + ASSERT(ref.ptr->name == name) + + return &ref.ptr->iprocess; +} diff --git a/external/badvpn_dns/ncd/NCDInterpProg.h b/external/badvpn_dns/ncd/NCDInterpProg.h new file mode 100644 index 00000000..2c8aaff7 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpProg.h @@ -0,0 +1,63 @@ +/** + * @file NCDInterpProg.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDINTERPPROG_H +#define BADVPN_NCDINTERPPROG_H + +#include +#include +#include +#include +#include +#include + +struct NCDInterpProg__process { + NCD_string_id_t name; + NCDInterpProcess iprocess; + int hash_next; +}; + +typedef struct NCDInterpProg__process NCDInterpProg__hashentry; +typedef struct NCDInterpProg__process *NCDInterpProg__hasharg; + +#include "NCDInterpProg_hash.h" +#include + +typedef struct { + struct NCDInterpProg__process *procs; + int num_procs; + NCDInterpProg__Hash hash; + DebugObject d_obj; +} NCDInterpProg; + +int NCDInterpProg_Init (NCDInterpProg *o, NCDProgram *prog, NCDStringIndex *string_index, NCDPlaceholderDb *pdb, NCDModuleIndex *module_index) WARN_UNUSED; +void NCDInterpProg_Free (NCDInterpProg *o); +NCDInterpProcess * NCDInterpProg_FindProcess (NCDInterpProg *o, NCD_string_id_t name); + +#endif diff --git a/external/badvpn_dns/ncd/NCDInterpProg_hash.h b/external/badvpn_dns/ncd/NCDInterpProg_hash.h new file mode 100644 index 00000000..fa8898ed --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpProg_hash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDInterpProg__Hash +#define CHASH_PARAM_ENTRY NCDInterpProg__hashentry +#define CHASH_PARAM_LINK int +#define CHASH_PARAM_KEY NCD_string_id_t +#define CHASH_PARAM_ARG NCDInterpProg__hasharg +#define CHASH_PARAM_NULL ((int)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) ((size_t)(entry).ptr->name) +#define CHASH_PARAM_KEYHASH(arg, key) ((size_t)(key)) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) ((entry1).ptr->name == (entry2).ptr->name) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) ((key1) == (entry2).ptr->name) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/external/badvpn_dns/ncd/NCDInterpreter.c b/external/badvpn_dns/ncd/NCDInterpreter.c new file mode 100644 index 00000000..dc05cc54 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpreter.c @@ -0,0 +1,1356 @@ +/** + * @file NCDInterpreter.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "NCDInterpreter.h" + +#include + +#define SSTATE_CHILD 0 +#define SSTATE_ADULT 1 +#define SSTATE_DYING 2 +#define SSTATE_FORGOTTEN 3 + +#define PSTATE_WORKING 0 +#define PSTATE_UP 1 +#define PSTATE_WAITING 2 +#define PSTATE_TERMINATING 3 + +struct statement { + NCDModuleInst inst; + NCDValMem args_mem; + int mem_size; + int i; +}; + +struct process { + NCDInterpreter *interp; + BReactor *reactor; + NCDInterpProcess *iprocess; + NCDModuleProcess *module_process; + BSmallTimer wait_timer; + BSmallPending work_job; + LinkedList1Node list_node; // node in processes + int ap; + int fp; + int num_statements; + unsigned int error:1; + unsigned int have_alloc:1; +#ifndef NDEBUG + int state; +#endif + struct statement statements[]; +}; + +static void start_terminate (NCDInterpreter *interp, int exit_code); +static char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del); +static void clear_process_cache (NCDInterpreter *interp); +static struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess); +static void process_release (struct process *p, int no_push); +static void process_assert_statements_cleared (struct process *p); +static int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process); +static void process_free (struct process *p, NCDModuleProcess **out_mp); +static void process_set_state (struct process *p, int state); +static void process_start_terminating (struct process *p); +static int process_have_child (struct process *p); +static void process_assert_pointers (struct process *p); +static void process_logfunc (struct process *p); +static void process_log (struct process *p, int level, const char *fmt, ...); +static void process_work_job_handler_working (struct process *p); +static void process_work_job_handler_up (struct process *p); +static void process_work_job_handler_waiting (struct process *p); +static void process_work_job_handler_terminating (struct process *p); +static int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out); +static void process_advance (struct process *p); +static void process_wait_timer_handler (BSmallTimer *timer); +static int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object); +static int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object); +static int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value); +static void statement_logfunc (struct statement *ps); +static void statement_log (struct statement *ps, int level, const char *fmt, ...); +static struct process * statement_process (struct statement *ps); +static int statement_mem_is_allocated (struct statement *ps); +static int statement_mem_size (struct statement *ps); +static int statement_allocate_memory (struct statement *ps, int alloc_size); +static void statement_instance_func_event (NCDModuleInst *inst, int event); +static int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object); +static int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess *mp, NCD_string_id_t template_name); +static void statement_instance_logfunc (NCDModuleInst *inst); +static void statement_instance_func_interp_exit (void *vinterp, int exit_code); +static int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value); +static btime_t statement_instance_func_interp_getretrytime (void *vinterp); +static int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group); +static void process_moduleprocess_func_event (struct process *p, int event); +static int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object); + +#define STATEMENT_LOG(ps, channel, ...) if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, channel)) statement_log(ps, channel, __VA_ARGS__) + +int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterpreter_params params) +{ + ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE)); + ASSERT(!NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE_GUARD)); + ASSERT(params.handler_finished); + ASSERT(params.num_extra_args >= 0); + ASSERT(params.reactor); +#ifndef BADVPN_NO_PROCESS + ASSERT(params.manager); +#endif +#ifndef BADVPN_NO_UDEV + ASSERT(params.umanager); +#endif +#ifndef BADVPN_NO_RANDOM + ASSERT(params.random2); +#endif + + // set params + o->params = params; + + // set not terminating + o->terminating = 0; + + // set program + o->program = program; + + // init string index + if (!NCDStringIndex_Init(&o->string_index)) { + BLog(BLOG_ERROR, "NCDStringIndex_Init failed"); + goto fail0; + } + + // init module index + if (!NCDModuleIndex_Init(&o->mindex, &o->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_Init failed"); + goto fail2; + } + + // init pointers to global resources in out struct NCDModuleInst_iparams. + // Don't initialize any callback at this point as these must not be called + // from globalinit functions of modules. + o->module_iparams.reactor = params.reactor; +#ifndef BADVPN_NO_PROCESS + o->module_iparams.manager = params.manager; +#endif +#ifndef BADVPN_NO_UDEV + o->module_iparams.umanager = params.umanager; +#endif +#ifndef BADVPN_NO_RANDOM + o->module_iparams.random2 = params.random2; +#endif + o->module_iparams.string_index = &o->string_index; + + // add module groups to index and allocate string id's for base_type's + for (const struct NCDModuleGroup **g = ncd_modules; *g; g++) { + if (!NCDModuleIndex_AddGroup(&o->mindex, *g, &o->module_iparams, &o->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed"); + goto fail3; + } + } + + // desugar + if (!NCDSugar_Desugar(&o->program)) { + BLog(BLOG_ERROR, "NCDSugar_Desugar failed"); + goto fail3; + } + + // init placeholder database + if (!NCDPlaceholderDb_Init(&o->placeholder_db, &o->string_index)) { + BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed"); + goto fail3; + } + + // init interp program + if (!NCDInterpProg_Init(&o->iprogram, &o->program, &o->string_index, &o->placeholder_db, &o->mindex)) { + BLog(BLOG_ERROR, "NCDInterpProg_Init failed"); + goto fail5; + } + + // init the rest of the module parameters structures + o->module_params.func_event = statement_instance_func_event; + o->module_params.func_getobj = statement_instance_func_getobj; + o->module_params.logfunc = (BLog_logfunc)statement_instance_logfunc; + o->module_params.iparams = &o->module_iparams; + o->module_iparams.user = o; + o->module_iparams.func_initprocess = statement_instance_func_initprocess; + o->module_iparams.func_interp_exit = statement_instance_func_interp_exit; + o->module_iparams.func_interp_getargs = statement_instance_func_interp_getargs; + o->module_iparams.func_interp_getretrytime = statement_instance_func_interp_getretrytime; + o->module_iparams.func_loadgroup = statement_instance_func_interp_loadgroup; + + // init processes list + LinkedList1_Init(&o->processes); + + // init processes + for (NCDProgramElem *elem = NCDProgram_FirstElem(&o->program); elem; elem = NCDProgram_NextElem(&o->program, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *p = NCDProgramElem_Process(elem); + + if (NCDProcess_IsTemplate(p)) { + continue; + } + + // get string id for process name + NCD_string_id_t name_id = NCDStringIndex_Lookup(&o->string_index, NCDProcess_Name(p)); + ASSERT(name_id >= 0) + + // find iprocess + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&o->iprogram, name_id); + ASSERT(iprocess) + + if (!process_new(o, iprocess, NULL)) { + BLog(BLOG_ERROR, "failed to initialize process, exiting"); + goto fail7; + } + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail7:; + // free processes + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->processes)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor)); + NCDModuleProcess *mp; + process_free(p, &mp); + ASSERT(!mp) + } + // clear process cache (process_free() above may push to cache) + clear_process_cache(o); + // free interp program + NCDInterpProg_Free(&o->iprogram); +fail5: + // free placeholder database + NCDPlaceholderDb_Free(&o->placeholder_db); +fail3: + // free module index + NCDModuleIndex_Free(&o->mindex); +fail2: + // free string index + NCDStringIndex_Free(&o->string_index); +fail0: + // free program AST + NCDProgram_Free(&o->program); + return 0; +} + +void NCDInterpreter_Free (NCDInterpreter *o) +{ + DebugObject_Free(&o->d_obj); + // any process that exists must be completely uninitialized + + // free processes + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->processes)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + BSmallPending_Unset(&p->work_job, BReactor_PendingGroup(o->params.reactor)); + NCDModuleProcess *mp; + process_free(p, &mp); + ASSERT(!mp) + } + + // clear process cache + clear_process_cache(o); + + // free interp program + NCDInterpProg_Free(&o->iprogram); + + // free placeholder database + NCDPlaceholderDb_Free(&o->placeholder_db); + + // free module index + NCDModuleIndex_Free(&o->mindex); + + // free string index + NCDStringIndex_Free(&o->string_index); + + // free program AST + NCDProgram_Free(&o->program); +} + +void NCDInterpreter_RequestShutdown (NCDInterpreter *o, int exit_code) +{ + DebugObject_Access(&o->d_obj); + + start_terminate(o, exit_code); +} + +void start_terminate (NCDInterpreter *interp, int exit_code) +{ + // remember exit code + interp->main_exit_code = exit_code; + + // if we're already terminating, there's nothing to do + if (interp->terminating) { + return; + } + + // set the terminating flag + interp->terminating = 1; + + // if there are no processes, we're done + if (LinkedList1_IsEmpty(&interp->processes)) { + interp->params.handler_finished(interp->params.user, interp->main_exit_code); + return; + } + + // start terminating non-template processes + for (LinkedList1Node *ln = LinkedList1_GetFirst(&interp->processes); ln; ln = LinkedList1Node_Next(ln)) { + struct process *p = UPPER_OBJECT(ln, struct process, list_node); + if (p->module_process) { + continue; + } + process_start_terminating(p); + } +} + +char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del) +{ + ExpString str; + if (!ExpString_Init(&str)) { + goto fail0; + } + + int is_first = 1; + + while (num_names > 0) { + if (!is_first && !ExpString_AppendChar(&str, del)) { + goto fail1; + } + const char *name_str = NCDStringIndex_Value(&interp->string_index, *names); + if (!ExpString_Append(&str, name_str)) { + goto fail1; + } + names++; + num_names--; + is_first = 0; + } + + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +void clear_process_cache (NCDInterpreter *interp) +{ + for (NCDProgramElem *elem = NCDProgram_FirstElem(&interp->program); elem; elem = NCDProgram_NextElem(&interp->program, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *ast_p = NCDProgramElem_Process(elem); + + NCD_string_id_t name_id = NCDStringIndex_Lookup(&interp->string_index, NCDProcess_Name(ast_p)); + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, name_id); + + struct process *p; + while (p = NCDInterpProcess_CachePull(iprocess)) { + process_release(p, 1); + } + } +} + +struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess) +{ + ASSERT(iprocess) + + // try to pull from cache + struct process *p = NCDInterpProcess_CachePull(iprocess); + if (p) { + goto allocated; + } + + // get number of statements + int num_statements = NCDInterpProcess_NumStatements(iprocess); + + // get size of preallocated memory + int mem_size = NCDInterpProcess_PreallocSize(iprocess); + if (mem_size < 0) { + goto fail0; + } + + // start with size of process structure + size_t alloc_size = sizeof(struct process); + + // add size of statements array + if (num_statements > SIZE_MAX / sizeof(struct statement)) { + goto fail0; + } + if (!BSizeAdd(&alloc_size, num_statements * sizeof(struct statement))) { + goto fail0; + } + + // align for preallocated memory + if (!BSizeAlign(&alloc_size, BMAX_ALIGN)) { + goto fail0; + } + size_t mem_off = alloc_size; + + // add size of preallocated memory + if (mem_size > SIZE_MAX || !BSizeAdd(&alloc_size, mem_size)) { + goto fail0; + } + + // allocate memory + p = BAlloc(alloc_size); + if (!p) { + goto fail0; + } + + // set variables + p->interp = interp; + p->reactor = interp->params.reactor; + p->iprocess = iprocess; + p->ap = 0; + p->fp = 0; + p->num_statements = num_statements; + p->error = 0; + p->have_alloc = 0; + + // init statements + char *mem = (char *)p + mem_off; + for (int i = 0; i < num_statements; i++) { + struct statement *ps = &p->statements[i]; + ps->i = i; + ps->inst.istate = SSTATE_FORGOTTEN; + ps->mem_size = NCDInterpProcess_StatementPreallocSize(iprocess, i); + ps->inst.mem = mem + NCDInterpProcess_StatementPreallocOffset(iprocess, i); + } + + // init timer + BSmallTimer_Init(&p->wait_timer, process_wait_timer_handler); + + // init work job + BSmallPending_Init(&p->work_job, BReactor_PendingGroup(p->reactor), NULL, NULL); + +allocated: + ASSERT(p->interp == interp) + ASSERT(p->reactor == interp->params.reactor) + ASSERT(p->iprocess == iprocess) + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(p->num_statements == NCDInterpProcess_NumStatements(iprocess)) + ASSERT(p->error == 0) + process_assert_statements_cleared(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(!BSmallTimer_IsRunning(&p->wait_timer)) + + return p; + +fail0: + BLog(BLOG_ERROR, "failed to allocate memory for process %s", NCDInterpProcess_Name(iprocess)); + return NULL; +} + +void process_release (struct process *p, int no_push) +{ + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(p->error == 0) + process_assert_statements_cleared(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(!BSmallTimer_IsRunning(&p->wait_timer)) + + // try to push to cache + if (!no_push && !p->have_alloc) { + if (NCDInterpProcess_CachePush(p->iprocess, p)) { + return; + } + } + + // free work job + BSmallPending_Free(&p->work_job, BReactor_PendingGroup(p->reactor)); + + // free statement memory + if (p->have_alloc) { + for (int i = 0; i < p->num_statements; i++) { + struct statement *ps = &p->statements[i]; + if (statement_mem_is_allocated(ps)) { + free(ps->inst.mem); + } + } + } + + // free strucure + BFree(p); +} + +void process_assert_statements_cleared (struct process *p) +{ +#ifndef NDEBUG + for (int i = 0; i < p->num_statements; i++) { + ASSERT(p->statements[i].i == i) + ASSERT(p->statements[i].inst.istate == SSTATE_FORGOTTEN) + } +#endif +} + +int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process) +{ + ASSERT(iprocess) + + // allocate prepared process struct + struct process *p = process_allocate(interp, iprocess); + if (!p) { + return 0; + } + + // set module process pointer + p->module_process = module_process; + + // set module process handlers + if (p->module_process) { + NCDModuleProcess_Interp_SetHandlers(p->module_process, p, + (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event, + (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj); + } + + // set state + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // insert to processes list + LinkedList1_Append(&interp->processes, &p->list_node); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + + return 1; +} + +void process_set_state (struct process *p, int state) +{ +#ifndef NDEBUG + p->state = state; +#endif +} + +void process_free (struct process *p, NCDModuleProcess **out_mp) +{ + ASSERT(p->ap == 0) + ASSERT(p->fp == 0) + ASSERT(out_mp) + ASSERT(!BSmallPending_IsSet(&p->work_job)) + + // give module process to caller so it can inform the process creator that the process has terminated + *out_mp = p->module_process; + + // remove from processes list + LinkedList1_Remove(&p->interp->processes, &p->list_node); + + // free timer + BReactor_RemoveSmallTimer(p->reactor, &p->wait_timer); + + // clear error + p->error = 0; + + process_release(p, 0); +} + +void process_start_terminating (struct process *p) +{ + // set state terminating + process_set_state(p, PSTATE_TERMINATING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_terminating, p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); +} + +int process_have_child (struct process *p) +{ + return (p->ap > 0 && p->statements[p->ap - 1].inst.istate == SSTATE_CHILD); +} + +void process_assert_pointers (struct process *p) +{ + ASSERT(p->ap <= p->num_statements) + ASSERT(p->fp >= p->ap) + ASSERT(p->fp <= p->num_statements) + +#ifndef NDEBUG + // check AP + for (int i = 0; i < p->ap; i++) { + if (i == p->ap - 1) { + ASSERT(p->statements[i].inst.istate == SSTATE_ADULT || p->statements[i].inst.istate == SSTATE_CHILD) + } else { + ASSERT(p->statements[i].inst.istate == SSTATE_ADULT) + } + } + + // check FP + int fp = p->num_statements; + while (fp > 0 && p->statements[fp - 1].inst.istate == SSTATE_FORGOTTEN) { + fp--; + } + ASSERT(p->fp == fp) +#endif +} + +void process_logfunc (struct process *p) +{ + BLog_Append("process %s: ", NCDInterpProcess_Name(p->iprocess)); +} + +void process_log (struct process *p, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)process_logfunc, p, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void process_work_job_handler_working (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_WORKING) + + // cleaning up? + if (p->ap < p->fp) { + // order the last living statement to die, if needed + struct statement *ps = &p->statements[p->fp - 1]; + if (ps->inst.istate == SSTATE_DYING) { + return; + } + + STATEMENT_LOG(ps, BLOG_INFO, "killing"); + + // set statement state DYING + ps->inst.istate = SSTATE_DYING; + + // order it to die + NCDModuleInst_Die(&ps->inst); + return; + } + + // clean? + if (process_have_child(p)) { + ASSERT(p->ap > 0) + ASSERT(p->ap <= p->num_statements) + + struct statement *ps = &p->statements[p->ap - 1]; + ASSERT(ps->inst.istate == SSTATE_CHILD) + + STATEMENT_LOG(ps, BLOG_INFO, "clean"); + + // report clean + NCDModuleInst_Clean(&ps->inst); + return; + } + + // finished? + if (p->ap == p->num_statements) { + process_log(p, BLOG_INFO, "victory"); + + // set state up + process_set_state(p, PSTATE_UP); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_up, p); + + // set module process up + if (p->module_process) { + NCDModuleProcess_Interp_Up(p->module_process); + } + return; + } + + // advancing? + struct statement *ps = &p->statements[p->ap]; + ASSERT(ps->inst.istate == SSTATE_FORGOTTEN) + + if (p->error) { + STATEMENT_LOG(ps, BLOG_INFO, "waiting after error"); + + // clear error + p->error = 0; + + // set wait timer + BReactor_SetSmallTimer(p->reactor, &p->wait_timer, BTIMER_SET_RELATIVE, p->interp->params.retry_time); + } else { + // advance + process_advance(p); + } +} + +void process_work_job_handler_up (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_UP) + ASSERT(p->ap < p->num_statements || process_have_child(p)) + + // if we have module process, wait for its permission to continue + if (p->module_process) { + // set state waiting + process_set_state(p, PSTATE_WAITING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_waiting, p); + + // set module process down + NCDModuleProcess_Interp_Down(p->module_process); + return; + } + + // set state working + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // delegate the rest to the working handler + process_work_job_handler_working(p); +} + +void process_work_job_handler_waiting (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_WAITING) + + // do absolutely nothing. Having this no-op handler avoids a branch + // in statement_instance_func_event(). +} + +void process_work_job_handler_terminating (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->state == PSTATE_TERMINATING) + +again: + if (p->fp == 0) { + NCDInterpreter *interp = p->interp; + + // free process + NCDModuleProcess *mp; + process_free(p, &mp); + + // if program is terminating amd there are no more processes, exit program + if (interp->terminating && LinkedList1_IsEmpty(&interp->processes)) { + ASSERT(!mp) + interp->params.handler_finished(interp->params.user, interp->main_exit_code); + return; + } + + // inform the process creator that the process has terminated + if (mp) { + NCDModuleProcess_Interp_Terminated(mp); + return; + } + + return; + } + + // order the last living statement to die, if needed + struct statement *ps = &p->statements[p->fp - 1]; + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + if (ps->inst.istate == SSTATE_DYING) { + return; + } + + STATEMENT_LOG(ps, BLOG_INFO, "killing"); + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // optimize for statements which can be destroyed immediately + if (NCDModuleInst_TryFree(&ps->inst)) { + STATEMENT_LOG(ps, BLOG_INFO, "died"); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set statement state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + + goto again; + } + + // set statement state DYING + ps->inst.istate = SSTATE_DYING; + + // order it to die + NCDModuleInst_Die(&ps->inst); + return; +} + +int replace_placeholders_callback (void *arg, int plid, NCDValMem *mem, NCDValRef *out) +{ + struct process *p = arg; + ASSERT(plid >= 0) + ASSERT(mem) + ASSERT(out) + + const NCD_string_id_t *varnames; + size_t num_names; + NCDPlaceholderDb_GetVariable(&p->interp->placeholder_db, plid, &varnames, &num_names); + + return process_resolve_variable_expr(p, p->ap, varnames, num_names, mem, out); +} + +void process_advance (struct process *p) +{ + process_assert_pointers(p); + ASSERT(p->ap == p->fp) + ASSERT(!process_have_child(p)) + ASSERT(p->ap < p->num_statements) + ASSERT(!p->error) + ASSERT(!BSmallPending_IsSet(&p->work_job)) + ASSERT(p->state == PSTATE_WORKING) + + struct statement *ps = &p->statements[p->ap]; + ASSERT(ps->inst.istate == SSTATE_FORGOTTEN) + + STATEMENT_LOG(ps, BLOG_INFO, "initializing"); + + // need to determine the module and object to use it on (if it's a method) + const struct NCDInterpModule *module; + void *method_context = NULL; + + // get object names, e.g. "my.cat" in "my.cat->meow();" + // (or NULL if this is not a method statement) + const NCD_string_id_t *objnames; + size_t num_objnames; + NCDInterpProcess_StatementObjNames(p->iprocess, p->ap, &objnames, &num_objnames); + + if (!objnames) { + // not a method; module is already known by NCDInterpProcess + module = NCDInterpProcess_StatementGetSimpleModule(p->iprocess, p->ap, &p->interp->string_index, &p->interp->mindex); + + if (!module) { + const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index); + STATEMENT_LOG(ps, BLOG_ERROR, "unknown simple statement: %s", cmdname_str); + goto fail0; + } + } else { + // get object + NCDObject object; + if (!process_resolve_object_expr(p, p->ap, objnames, num_objnames, &object)) { + goto fail0; + } + + // get object type + NCD_string_id_t object_type = NCDObject_Type(&object); + if (object_type < 0) { + STATEMENT_LOG(ps, BLOG_ERROR, "cannot call method on object with no type"); + goto fail0; + } + + // get method context + method_context = NCDObject_MethodUser(&object); + + // find module based on type of object + module = NCDInterpProcess_StatementGetMethodModule(p->iprocess, p->ap, object_type, &p->interp->mindex); + + if (!module) { + const char *type_str = NCDStringIndex_Value(&p->interp->string_index, object_type); + const char *cmdname_str = NCDInterpProcess_StatementCmdName(p->iprocess, p->ap, &p->interp->string_index); + STATEMENT_LOG(ps, BLOG_ERROR, "unknown method statement: %s::%s", type_str, cmdname_str); + goto fail0; + } + } + + // copy arguments + NCDValRef args; + NCDValReplaceProg prog; + if (!NCDInterpProcess_CopyStatementArgs(p->iprocess, ps->i, &ps->args_mem, &args, &prog)) { + STATEMENT_LOG(ps, BLOG_ERROR, "NCDInterpProcess_CopyStatementArgs failed"); + goto fail0; + } + + // replace placeholders with values of variables + if (!NCDValReplaceProg_Execute(prog, &ps->args_mem, replace_placeholders_callback, p)) { + STATEMENT_LOG(ps, BLOG_ERROR, "failed to replace variables in arguments with values"); + goto fail1; + } + + // convert non-continuous strings unless the module can handle them + if (!(module->module.flags & NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS)) { + if (!NCDValMem_ConvertNonContinuousStrings(&ps->args_mem, &args)) { + STATEMENT_LOG(ps, BLOG_ERROR, "NCDValMem_ConvertNonContinuousStrings failed"); + goto fail1; + } + } + + // allocate memory + if (!statement_allocate_memory(ps, module->module.alloc_size)) { + STATEMENT_LOG(ps, BLOG_ERROR, "failed to allocate memory"); + goto fail1; + } + + // set statement state CHILD + ps->inst.istate = SSTATE_CHILD; + + // increment AP + p->ap++; + + // increment FP + p->fp++; + + process_assert_pointers(p); + + // initialize module instance + NCDModuleInst_Init(&ps->inst, module, method_context, args, &p->interp->module_params); + return; + +fail1: + NCDValMem_Free(&ps->args_mem); +fail0: + // set error + p->error = 1; + + // schedule work to start the timer + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); +} + +void process_wait_timer_handler (BSmallTimer *timer) +{ + struct process *p = UPPER_OBJECT(timer, struct process, wait_timer); + process_assert_pointers(p); + ASSERT(!BSmallPending_IsSet(&p->work_job)) + + // check if something happened that means we no longer need to retry + if (p->ap != p->fp || process_have_child(p) || p->ap == p->num_statements) { + return; + } + + process_log(p, BLOG_INFO, "retrying"); + + // advance. Note: the asserts for this are indeed satisfied, though this + // it not trivial to prove. + process_advance(p); +} + +int process_find_object (struct process *p, int pos, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(out_object) + + int i = NCDInterpProcess_FindStatement(p->iprocess, pos, name); + if (i >= 0) { + struct statement *ps = &p->statements[i]; + ASSERT(i < p->num_statements) + + if (ps->inst.istate == SSTATE_FORGOTTEN) { + process_log(p, BLOG_ERROR, "statement (%d) is uninitialized", i); + return 0; + } + + *out_object = NCDModuleInst_Object(&ps->inst); + return 1; + } + + if (p->module_process && NCDModuleProcess_Interp_GetSpecialObj(p->module_process, name, out_object)) { + return 1; + } + + return 0; +} + +int process_resolve_object_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(names) + ASSERT(num_names > 0) + ASSERT(out_object) + + NCDObject object; + if (!process_find_object(p, pos, names[0], &object)) { + goto fail; + } + + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, num_names - 1, out_object)) { + goto fail; + } + + return 1; + +fail:; + char *name = implode_id_strings(p->interp, names, num_names, '.'); + process_log(p, BLOG_ERROR, "failed to resolve object (%s) from position %zu", (name ? name : ""), pos); + free(name); + return 0; +} + +int process_resolve_variable_expr (struct process *p, int pos, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(pos >= 0) + ASSERT(pos <= p->num_statements) + ASSERT(names) + ASSERT(num_names > 0) + ASSERT(mem) + ASSERT(out_value) + + NCDObject object; + if (!process_find_object(p, pos, names[0], &object)) { + goto fail; + } + + if (!NCDObject_ResolveVarExprCompact(&object, names + 1, num_names - 1, mem, out_value)) { + goto fail; + } + + return 1; + +fail:; + char *name = implode_id_strings(p->interp, names, num_names, '.'); + process_log(p, BLOG_ERROR, "failed to resolve variable (%s) from position %zu", (name ? name : ""), pos); + free(name); + return 0; +} + +void statement_logfunc (struct statement *ps) +{ + process_logfunc(statement_process(ps)); + BLog_Append("statement %zu: ", ps->i); +} + +void statement_log (struct statement *ps, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)statement_logfunc, ps, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +struct process * statement_process (struct statement *ps) +{ + return UPPER_OBJECT(ps - ps->i, struct process, statements); +} + +int statement_mem_is_allocated (struct statement *ps) +{ + return (ps->mem_size < 0); +} + +int statement_mem_size (struct statement *ps) +{ + return (ps->mem_size >= 0 ? ps->mem_size : -ps->mem_size); +} + +int statement_allocate_memory (struct statement *ps, int alloc_size) +{ + ASSERT(alloc_size >= 0) + + if (alloc_size > statement_mem_size(ps)) { + // allocate new memory + char *new_mem = malloc(alloc_size); + if (!new_mem) { + STATEMENT_LOG(ps, BLOG_ERROR, "malloc failed"); + return 0; + } + + // release old memory unless it was preallocated + if (statement_mem_is_allocated(ps)) { + free(ps->inst.mem); + } + + struct process *p = statement_process(ps); + + // register memory in statement + ps->inst.mem = new_mem; + ps->mem_size = -alloc_size; + + // set the alloc flag in the process to make sure process_free() + // releases the allocated memory + p->have_alloc = 1; + + // register alloc size for future preallocations + NCDInterpProcess_StatementBumpAllocSize(p->iprocess, ps->i, alloc_size); + } + + return 1; +} + +void statement_instance_func_event (NCDModuleInst *inst, int event) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate == SSTATE_CHILD || ps->inst.istate == SSTATE_ADULT || ps->inst.istate == SSTATE_DYING) + + struct process *p = statement_process(ps); + process_assert_pointers(p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + + switch (event) { + case NCDMODULE_EVENT_UP: { + ASSERT(ps->inst.istate == SSTATE_CHILD) + + STATEMENT_LOG(ps, BLOG_INFO, "up"); + + // set state ADULT + ps->inst.istate = SSTATE_ADULT; + } break; + + case NCDMODULE_EVENT_DOWN: { + ASSERT(ps->inst.istate == SSTATE_ADULT) + + STATEMENT_LOG(ps, BLOG_INFO, "down"); + + // set state CHILD + ps->inst.istate = SSTATE_CHILD; + + // clear error + if (ps->i < p->ap) { + p->error = 0; + } + + // update AP + if (p->ap > ps->i + 1) { + p->ap = ps->i + 1; + } + } break; + + case NCDMODULE_EVENT_DOWNUP: { + ASSERT(ps->inst.istate == SSTATE_ADULT) + + STATEMENT_LOG(ps, BLOG_INFO, "down"); + STATEMENT_LOG(ps, BLOG_INFO, "up"); + + // clear error + if (ps->i < p->ap) { + p->error = 0; + } + + // update AP + if (p->ap > ps->i + 1) { + p->ap = ps->i + 1; + } + } break; + + case NCDMODULE_EVENT_DEAD: { + STATEMENT_LOG(ps, BLOG_INFO, "died"); + + // free instance + NCDModuleInst_Free(&ps->inst); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + } break; + + case NCDMODULE_EVENT_DEADERROR: { + STATEMENT_LOG(ps, BLOG_ERROR, "died with error"); + + // free instance + NCDModuleInst_Free(&ps->inst); + + // free arguments memory + NCDValMem_Free(&ps->args_mem); + + // set state FORGOTTEN + ps->inst.istate = SSTATE_FORGOTTEN; + + // set error + if (ps->i < p->ap) { + p->error = 1; + } + + // update AP + if (p->ap > ps->i) { + p->ap = ps->i; + } + + // update FP + while (p->fp > 0 && p->statements[p->fp - 1].inst.istate == SSTATE_FORGOTTEN) { + p->fp--; + } + } break; + } +} + +int statement_instance_func_getobj (NCDModuleInst *inst, NCD_string_id_t objname, NCDObject *out_object) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + + return process_find_object(statement_process(ps), ps->i, objname, out_object); +} + +int statement_instance_func_initprocess (void *vinterp, NCDModuleProcess* mp, NCD_string_id_t template_name) +{ + NCDInterpreter *interp = vinterp; + + // find process + NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, template_name); + if (!iprocess) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "no template named %s", str); + return 0; + } + + // make sure it's a template + if (!NCDInterpProcess_IsTemplate(iprocess)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "need template to create a process, but %s is a process", str); + return 0; + } + + // create process + if (!process_new(interp, iprocess, mp)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_ERROR, "failed to create process from template %s", str); + return 0; + } + + if (BLog_WouldLog(BLOG_INFO, BLOG_CURRENT_CHANNEL)) { + const char *str = NCDStringIndex_Value(&interp->string_index, template_name); + BLog(BLOG_INFO, "created process from template %s", str); + } + + return 1; +} + +void statement_instance_logfunc (NCDModuleInst *inst) +{ + struct statement *ps = UPPER_OBJECT(inst, struct statement, inst); + ASSERT(ps->inst.istate != SSTATE_FORGOTTEN) + + statement_logfunc(ps); + BLog_Append("module: "); +} + +void statement_instance_func_interp_exit (void *vinterp, int exit_code) +{ + NCDInterpreter *interp = vinterp; + + start_terminate(interp, exit_code); +} + +int statement_instance_func_interp_getargs (void *vinterp, NCDValMem *mem, NCDValRef *out_value) +{ + NCDInterpreter *interp = vinterp; + + *out_value = NCDVal_NewList(mem, interp->params.num_extra_args); + if (NCDVal_IsInvalid(*out_value)) { + BLog(BLOG_ERROR, "NCDVal_NewList failed"); + goto fail; + } + + for (int i = 0; i < interp->params.num_extra_args; i++) { + NCDValRef arg = NCDVal_NewString(mem, interp->params.extra_args[i]); + if (NCDVal_IsInvalid(arg)) { + BLog(BLOG_ERROR, "NCDVal_NewString failed"); + goto fail; + } + + if (!NCDVal_ListAppend(*out_value, arg)) { + BLog(BLOG_ERROR, "depth limit exceeded"); + goto fail; + } + } + + return 1; + +fail: + *out_value = NCDVal_NewInvalid(); + return 1; +} + +btime_t statement_instance_func_interp_getretrytime (void *vinterp) +{ + NCDInterpreter *interp = vinterp; + + return interp->params.retry_time; +} + +int statement_instance_func_interp_loadgroup (void *vinterp, const struct NCDModuleGroup *group) +{ + NCDInterpreter *interp = vinterp; + + if (!NCDModuleIndex_AddGroup(&interp->mindex, group, &interp->module_iparams, &interp->string_index)) { + BLog(BLOG_ERROR, "NCDModuleIndex_AddGroup failed"); + return 0; + } + + return 1; +} + +void process_moduleprocess_func_event (struct process *p, int event) +{ + ASSERT(p->module_process) + + switch (event) { + case NCDMODULEPROCESS_INTERP_EVENT_CONTINUE: { + ASSERT(p->state == PSTATE_WAITING) + + // set state working + process_set_state(p, PSTATE_WORKING); + BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p); + + // schedule work + BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor)); + } break; + + case NCDMODULEPROCESS_INTERP_EVENT_TERMINATE: { + ASSERT(p->state != PSTATE_TERMINATING) + + process_log(p, BLOG_INFO, "process termination requested"); + + // start terminating + process_start_terminating(p); + } break; + + default: ASSERT(0); + } +} + +int process_moduleprocess_func_getobj (struct process *p, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(p->module_process) + + return process_find_object(p, p->num_statements, name, out_object); +} diff --git a/external/badvpn_dns/ncd/NCDInterpreter.h b/external/badvpn_dns/ncd/NCDInterpreter.h new file mode 100644 index 00000000..8d11c33d --- /dev/null +++ b/external/badvpn_dns/ncd/NCDInterpreter.h @@ -0,0 +1,156 @@ +/** + * @file NCDInterpreter.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_INTERPRETER_H +#define BADVPN_NCD_INTERPRETER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_NO_PROCESS +#include +#endif +#ifndef BADVPN_NO_UDEV +#include +#endif +#ifndef BADVPN_NO_RANDOM +#include +#endif + +/** + * Handler called when the interpreter has terminated, and {@link NCDInterpreter_Free} + * can be called. + * + * @param user the user member of struct {@link NCDInterpreter_params} + * @param exit_code the exit code specified in the last interpreter termination request + */ +typedef void (*NCDInterpreter_handler_finished) (void *user, int exit_code); + +struct NCDInterpreter_params { + // callbacks + NCDInterpreter_handler_finished handler_finished; + void *user; + + // options + btime_t retry_time; + char **extra_args; + int num_extra_args; + + // possibly shared resources + BReactor *reactor; +#ifndef BADVPN_NO_PROCESS + BProcessManager *manager; +#endif +#ifndef BADVPN_NO_UDEV + NCDUdevManager *umanager; +#endif +#ifndef BADVPN_NO_RANDOM + BRandom2 *random2; +#endif +}; + +typedef struct { + // parameters + struct NCDInterpreter_params params; + + // are we terminating + int terminating; + int main_exit_code; + + // string index + NCDStringIndex string_index; + + // module index + NCDModuleIndex mindex; + + // program AST + NCDProgram program; + + // placeholder database + NCDPlaceholderDb placeholder_db; + + // structure for efficient interpretation + NCDInterpProg iprogram; + + // common module parameters + struct NCDModuleInst_params module_params; + struct NCDModuleInst_iparams module_iparams; + + // processes + LinkedList1 processes; + + DebugObject d_obj; +} NCDInterpreter; + +/** + * Initializes and starts the interpreter. + * + * @param o the interpreter + * @param program the program to execute in AST format. The program must + * not contain any 'include' or 'include_guard' directives. + * The interpreter takes ownership of the program, regardless + * of the success of this function. + * @param params other parameters + * @return 1 on success, 0 on failure + */ +int NCDInterpreter_Init (NCDInterpreter *o, NCDProgram program, struct NCDInterpreter_params params) WARN_UNUSED; + +/** + * Frees the interpreter. + * This may only be called after the interpreter has terminated, i.e. + * the {@link NCDInterpreter_handler_finished} handler has been called. + * Additionally, it can be called right after {@link NCDInterpreter_Init} + * before any of the interpreter's {@link BPendingGroup} jobs have executed. + */ +void NCDInterpreter_Free (NCDInterpreter *o); + +/** + * Requests termination of the interpreter. + * NOTE: the program can request its own termination, possibly overriding the exit + * code specified here. Expect the program to terminate even if this function was + * not called. + * + * @param o the interpreter + * @param exit_code the exit code to be passed to {@link NCDInterpreter_handler_finished}. + * This overrides any exit code set previously. + */ +void NCDInterpreter_RequestShutdown (NCDInterpreter *o, int exit_code); + +#endif diff --git a/external/badvpn_dns/ncd/NCDMethodIndex.c b/external/badvpn_dns/ncd/NCDMethodIndex.c new file mode 100644 index 00000000..5b3662a9 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDMethodIndex.c @@ -0,0 +1,272 @@ +/** + * @file NCDMethodIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDMethodIndex.h" + +#include "NCDMethodIndex_hash.h" +#include + +#define GROWARRAY_NAME NamesArray +#define GROWARRAY_OBJECT_TYPE NCDMethodIndex +#define GROWARRAY_ARRAY_MEMBER names +#define GROWARRAY_CAPACITY_MEMBER names_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +#define GROWARRAY_NAME EntriesArray +#define GROWARRAY_OBJECT_TYPE NCDMethodIndex +#define GROWARRAY_ARRAY_MEMBER entries +#define GROWARRAY_CAPACITY_MEMBER entries_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +#include + +static int find_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx) +{ + ASSERT(method_name) + + NCDMethodIndex__HashRef ref = NCDMethodIndex__Hash_Lookup(&o->hash, o->names, method_name); + if (ref.link == -1) { + return 0; + } + + ASSERT(ref.link >= 0) + ASSERT(ref.link < o->num_names) + + struct NCDMethodIndex__method_name *name_entry = ref.ptr; + ASSERT(!strcmp(name_entry->method_name, method_name)) + ASSERT(name_entry->first_entry >= 0) + ASSERT(name_entry->first_entry < o->num_entries) + + if (out_entry_idx) { + *out_entry_idx = name_entry->first_entry; + } + return 1; +} + +static int add_method_name (NCDMethodIndex *o, const char *method_name, int *out_entry_idx) +{ + ASSERT(method_name) + ASSERT(!find_method_name(o, method_name, NULL)) + + if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed"); + goto fail0; + } + + if (o->num_names == o->names_capacity && !NamesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "NamesArray_DoubleUp failed"); + goto fail0; + } + + struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries]; + entry->obj_type = -1; + entry->next = -1; + + struct NCDMethodIndex__method_name *name_entry = &o->names[o->num_names]; + + if (!(name_entry->method_name = b_strdup(method_name))) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + + name_entry->first_entry = o->num_entries; + + NCDMethodIndex__HashRef ref = {name_entry, o->num_names}; + int res = NCDMethodIndex__Hash_Insert(&o->hash, o->names, ref, NULL); + ASSERT_EXECUTE(res) + + o->num_entries++; + o->num_names++; + + if (out_entry_idx) { + *out_entry_idx = name_entry->first_entry; + } + return 1; + +fail0: + return 0; +} + +int NCDMethodIndex_Init (NCDMethodIndex *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + o->string_index = string_index; + + if (!NamesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) { + BLog(BLOG_ERROR, "NamesArray_Init failed"); + goto fail0; + } + + if (!EntriesArray_Init(o, NCDMETHODINDEX_NUM_EXPECTED_ENTRIES)) { + BLog(BLOG_ERROR, "EntriesArray_Init failed"); + goto fail1; + } + + o->num_names = 0; + o->num_entries = 0; + + if (!NCDMethodIndex__Hash_Init(&o->hash, NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES)) { + BLog(BLOG_ERROR, "NCDMethodIndex__Hash_Init failed"); + goto fail2; + } + + return 1; + +fail2: + EntriesArray_Free(o); +fail1: + NamesArray_Free(o); +fail0: + return 0; +} + +void NCDMethodIndex_Free (NCDMethodIndex *o) +{ + for (int i = 0; i < o->num_names; i++) { + free(o->names[i].method_name); + } + + NCDMethodIndex__Hash_Free(&o->hash); + EntriesArray_Free(o); + NamesArray_Free(o); +} + +int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, size_t obj_type_len, const char *method_name, const struct NCDInterpModule *module) +{ + ASSERT(obj_type) + ASSERT(method_name) + ASSERT(module) + + NCD_string_id_t obj_type_id = NCDStringIndex_GetBin(o->string_index, obj_type, obj_type_len); + if (obj_type_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail0; + } + + int entry_idx; + int first_entry_idx; + + if (!find_method_name(o, method_name, &first_entry_idx)) { + if (!add_method_name(o, method_name, &entry_idx)) { + goto fail0; + } + + ASSERT(entry_idx >= 0) + ASSERT(entry_idx < o->num_entries) + + struct NCDMethodIndex__entry *entry = &o->entries[entry_idx]; + + entry->obj_type = obj_type_id; + entry->module = module; + } else { + ASSERT(first_entry_idx >= 0) + ASSERT(first_entry_idx < o->num_entries) + + if (o->num_entries == o->entries_capacity && !EntriesArray_DoubleUp(o)) { + BLog(BLOG_ERROR, "EntriesArray_DoubleUp failed"); + goto fail0; + } + + entry_idx = o->num_entries; + struct NCDMethodIndex__entry *entry = &o->entries[o->num_entries]; + + entry->obj_type = obj_type_id; + entry->module = module; + + entry->next = o->entries[first_entry_idx].next; + o->entries[first_entry_idx].next = o->num_entries; + + o->num_entries++; + } + + return entry_idx; + +fail0: + return -1; +} + +void NCDMethodIndex_RemoveMethod (NCDMethodIndex *o, int method_name_id) +{ + ASSERT(method_name_id >= 0) + ASSERT(method_name_id < o->num_entries) + ASSERT(o->entries[method_name_id].obj_type >= 0) + + o->entries[method_name_id].obj_type = -1; +} + +int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name) +{ + ASSERT(method_name) + + int first_entry_idx; + + if (!find_method_name(o, method_name, &first_entry_idx)) { + if (!add_method_name(o, method_name, &first_entry_idx)) { + return -1; + } + } + + ASSERT(first_entry_idx >= 0) + ASSERT(first_entry_idx < o->num_entries) + + return first_entry_idx; +} + +const struct NCDInterpModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, NCD_string_id_t obj_type, int method_name_id) +{ + ASSERT(obj_type >= 0) + ASSERT(method_name_id >= 0) + ASSERT(method_name_id < o->num_entries) + + do { + struct NCDMethodIndex__entry *entry = &o->entries[method_name_id]; + + if (entry->obj_type == obj_type) { + ASSERT(entry->module) + return entry->module; + } + + method_name_id = entry->next; + ASSERT(method_name_id >= -1) + ASSERT(method_name_id < o->num_entries) + } while (method_name_id >= 0); + + return NULL; +} diff --git a/external/badvpn_dns/ncd/NCDMethodIndex.h b/external/badvpn_dns/ncd/NCDMethodIndex.h new file mode 100644 index 00000000..e4db0a50 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDMethodIndex.h @@ -0,0 +1,135 @@ +/** + * @file NCDMethodIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDMETHODINDEX_H +#define BADVPN_NCDMETHODINDEX_H + +#include +#include +#include +#include + +#define NCDMETHODINDEX_NUM_EXPECTED_METHOD_NAMES 64 +#define NCDMETHODINDEX_NUM_EXPECTED_ENTRIES 64 + +struct NCDMethodIndex__method_name { + char *method_name; + int first_entry; + int hash_next; +}; + +struct NCDMethodIndex__entry { + NCD_string_id_t obj_type; + const struct NCDInterpModule *module; + int next; +}; + +typedef struct NCDMethodIndex__method_name NCDMethodIndex__hashentry; +typedef const char *NCDMethodIndex__hashkey; +typedef struct NCDMethodIndex__method_name *NCDMethodIndex__hasharg; + +#include "NCDMethodIndex_hash.h" +#include + +/** + * The method index associates (object_type, method_name) pairs to pointers + * to corresponding \link NCDInterpModule structures (whose type strings would + * be "object_type::method_name"). + * More precisely, the method names are represented as indices into an + * internal array, which allows very efficient lookup when the method names + * are known in advance, but not the object types. + */ +typedef struct { + struct NCDMethodIndex__method_name *names; + struct NCDMethodIndex__entry *entries; + int names_capacity; + int entries_capacity; + int num_names; + int num_entries; + NCDMethodIndex__Hash hash; + NCDStringIndex *string_index; +} NCDMethodIndex; + +/** + * Initializes the method index. + * + * @return 1 on success, 0 on failure + */ +int NCDMethodIndex_Init (NCDMethodIndex *o, NCDStringIndex *string_index) WARN_UNUSED; + +/** + * Frees the method index. + */ +void NCDMethodIndex_Free (NCDMethodIndex *o); + +/** + * Adds a method to the index. + * Duplicate methods will not be detected here. + * + * @param obj_type object type of method, e.g. "cat" in "cat::meow". + * Must not be NULL. Does not have to be null-terminated. + * @param obj_type_len number of characters in obj_type + * @param method_name name of method, e.g. "meow" in "cat::meow". + * Must not be NULL. + * @param module pointer to module structure. Must not be NULL. + * @return on success, a non-negative identifier; on failure, -1 + */ +int NCDMethodIndex_AddMethod (NCDMethodIndex *o, const char *obj_type, size_t obj_type_len, const char *method_name, const struct NCDInterpModule *module); + +/** + * Removes a method from the index. + * + * @param method_name_id method name identifier + */ +void NCDMethodIndex_RemoveMethod (NCDMethodIndex *o, int method_name_id); + +/** + * Obtains an internal integer identifier for a method name. The intention is that + * this is stored and later passed to \link NCDMethodIndex_GetMethodModule for + * efficient lookup of modules corresponding to methods. + * + * @param method_name name of method, e.g. "meow" in "cat::meow". + * Must not be NULL. + * @return non-negative integer on success, -1 on failure + */ +int NCDMethodIndex_GetMethodNameId (NCDMethodIndex *o, const char *method_name); + +/** + * Looks up the module corresponding to a method. The method name is passed as an + * identifier obtained from \link NCDMethodIndex_GetMethodNameId. + * + * @param obj_type object type of method, e.g. "cat" in "cat::meow", as a string + * identifier via {@link NCDStringIndex} + * @param method_name_id method name identifier. Must have been previously returned + * by a successfull call of \link NCDMethodIndex_GetMethodNameId. + * @return module pointer, or NULL if no such method exists + */ +const struct NCDInterpModule * NCDMethodIndex_GetMethodModule (NCDMethodIndex *o, NCD_string_id_t obj_type, int method_name_id); + +#endif diff --git a/external/badvpn_dns/ncd/NCDMethodIndex_hash.h b/external/badvpn_dns/ncd/NCDMethodIndex_hash.h new file mode 100644 index 00000000..f1108cb6 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDMethodIndex_hash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDMethodIndex__Hash +#define CHASH_PARAM_ENTRY NCDMethodIndex__hashentry +#define CHASH_PARAM_LINK int +#define CHASH_PARAM_KEY NCDMethodIndex__hashkey +#define CHASH_PARAM_ARG NCDMethodIndex__hasharg +#define CHASH_PARAM_NULL ((int)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) (badvpn_djb2_hash((const uint8_t *)(entry).ptr->method_name)) +#define CHASH_PARAM_KEYHASH(arg, key) (badvpn_djb2_hash((const uint8_t *)(key))) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->method_name, (entry2).ptr->method_name)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->method_name)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/external/badvpn_dns/ncd/NCDModule.c b/external/badvpn_dns/ncd/NCDModule.c new file mode 100644 index 00000000..da6894ac --- /dev/null +++ b/external/badvpn_dns/ncd/NCDModule.c @@ -0,0 +1,625 @@ +/** + * @file NCDModule.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define STATE_DEAD 0 +#define STATE_DOWN_CLEAN 1 +#define STATE_UP 2 +#define STATE_DOWN_UNCLEAN 3 +#define STATE_DYING 4 + +#define PROCESS_STATE_INIT 0 +#define PROCESS_STATE_DOWN 1 +#define PROCESS_STATE_UP 2 +#define PROCESS_STATE_DOWN_WAITING 3 +#define PROCESS_STATE_TERMINATING 4 +#define PROCESS_STATE_TERMINATED 5 + +static int object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static int object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int process_args_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static int process_arg_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +static void frontend_event (NCDModuleInst *n, int event) +{ + n->params->func_event(n, event); +} + +static void inst_assert_backend (NCDModuleInst *n) +{ + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) +} + +static void set_process_state (NCDModuleProcess *p, int state) +{ +#ifndef NDEBUG + p->state = state; +#endif +} + +void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void *method_context, NCDValRef args, const struct NCDModuleInst_params *params) +{ + ASSERT(m) + ASSERT(m->module.func_new2) + ASSERT(m->module.alloc_size >= 0) + ASSERT(m->base_type_id >= 0) + ASSERT(m->group) + ASSERT(n->mem) + ASSERT(NCDVal_IsList(args)) + ASSERT(params) + ASSERT(params->func_event) + ASSERT(params->func_getobj) + ASSERT(params->logfunc) + ASSERT(params->iparams) + ASSERT(params->iparams->func_initprocess) + ASSERT(params->iparams->func_interp_exit) + ASSERT(params->iparams->func_interp_getargs) + ASSERT(params->iparams->func_interp_getretrytime) + + // init arguments + n->m = m; + n->params = params; + + // set initial state + n->state = STATE_DOWN_CLEAN; + + // give NCDModuleInst to methods, not mem + n->pass_mem_to_methods = 0; + + DebugObject_Init(&n->d_obj); + + struct NCDModuleInst_new_params new_params; + new_params.method_user = method_context; + new_params.args = args; + + n->m->module.func_new2(n->mem, n, &new_params); +} + +void NCDModuleInst_Free (NCDModuleInst *n) +{ + DebugObject_Free(&n->d_obj); + ASSERT(n->state == STATE_DEAD) +} + +void NCDModuleInst_Die (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP || n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + n->state = STATE_DYING; + + if (!n->m->module.func_die) { + NCDModuleInst_Backend_Dead(n); + return; + } + + n->m->module.func_die(n->mem); + return; +} + +int NCDModuleInst_TryFree (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP || n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + if (n->m->module.func_die) { + return 0; + } + + DebugObject_Free(&n->d_obj); + + return 1; +} + +void NCDModuleInst_Clean (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + if (n->state == STATE_DOWN_UNCLEAN) { + n->state = STATE_DOWN_CLEAN; + + if (n->m->module.func_clean) { + n->m->module.func_clean(n->mem); + return; + } + } +} + +NCDObject NCDModuleInst_Object (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->m->base_type_id >= 0) + + void *method_user = (n->pass_mem_to_methods ? n->mem : n); + + return NCDObject_BuildFull(n->m->base_type_id, n, 0, method_user, object_func_getvar, object_func_getobj); +} + +void NCDModuleInst_Backend_PassMemToMethods (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + + n->pass_mem_to_methods = 1; +} + +static int can_resolve (NCDModuleInst *n) +{ + switch (n->state) { + case STATE_UP: + return 1; + case STATE_DOWN_CLEAN: + case STATE_DOWN_UNCLEAN: + return !!(n->m->module.flags & NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN); + default: + return 0; + } +} + +static int object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleInst *n = NCDObject_DataPtr(obj); + DebugObject_Access(&n->d_obj); + + if ((!n->m->module.func_getvar && !n->m->module.func_getvar2) || !can_resolve(n)) { + return 0; + } + + int res; + if (n->m->module.func_getvar2) { + res = n->m->module.func_getvar2(n->mem, name, mem, out_value); + } else { + if (NCDStringIndex_HasNulls(n->params->iparams->string_index, name)) { + return 0; + } + const char *name_str = NCDStringIndex_Value(n->params->iparams->string_index, name); + res = n->m->module.func_getvar(n->mem, name_str, mem, out_value); + } + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +static int object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + NCDModuleInst *n = NCDObject_DataPtr(obj); + DebugObject_Access(&n->d_obj); + + if (!n->m->module.func_getobj || !can_resolve(n)) { + return 0; + } + + int res = n->m->module.func_getobj(n->mem, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +void * NCDModuleInst_Backend_GetUser (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + + return n->mem; +} + +void NCDModuleInst_Backend_Up (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN) + + n->state = STATE_UP; + frontend_event(n, NCDMODULE_EVENT_UP); +} + +void NCDModuleInst_Backend_Down (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP) + + n->state = STATE_DOWN_UNCLEAN; + frontend_event(n, NCDMODULE_EVENT_DOWN); +} + +void NCDModuleInst_Backend_DownUp (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_UP) + + frontend_event(n, NCDMODULE_EVENT_DOWNUP); +} + +void NCDModuleInst_Backend_Dead (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN || + n->state == STATE_UP || n->state == STATE_DYING) + + n->state = STATE_DEAD; + + frontend_event(n, NCDMODULE_EVENT_DEAD); + return; +} + +void NCDModuleInst_Backend_DeadError (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_CLEAN || n->state == STATE_DOWN_UNCLEAN || + n->state == STATE_UP || n->state == STATE_DYING) + + n->state = STATE_DEAD; + + frontend_event(n, NCDMODULE_EVENT_DEADERROR); + return; +} + +int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(out_object) + + int res = n->params->func_getobj(n, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...) +{ + DebugObject_Access(&n->d_obj); + + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg(n->params->logfunc, n, channel, level, fmt, vl); + va_end(vl); +} + +void NCDModuleInst_Backend_LogVarArg (NCDModuleInst *n, int channel, int level, const char *fmt, va_list vl) +{ + DebugObject_Access(&n->d_obj); + + BLog_LogViaFuncVarArg(n->params->logfunc, n, channel, level, fmt, vl); +} + +BLogContext NCDModuleInst_Backend_LogContext (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + + return BLog_MakeContext(n->params->logfunc, n); +} + +void NCDModuleInst_Backend_InterpExit (NCDModuleInst *n, int exit_code) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + + n->params->iparams->func_interp_exit(n->params->iparams->user, exit_code); +} + +int NCDModuleInst_Backend_InterpGetArgs (NCDModuleInst *n, NCDValMem *mem, NCDValRef *out_value) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + ASSERT(mem) + ASSERT(out_value) + + int res = n->params->iparams->func_interp_getargs(n->params->iparams->user, mem, out_value); + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +btime_t NCDModuleInst_Backend_InterpGetRetryTime (NCDModuleInst *n) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + + return n->params->iparams->func_interp_getretrytime(n->params->iparams->user); +} + +int NCDModuleInst_Backend_InterpLoadGroup (NCDModuleInst *n, const struct NCDModuleGroup *group) +{ + DebugObject_Access(&n->d_obj); + inst_assert_backend(n); + ASSERT(group) + + return n->params->iparams->func_loadgroup(n->params->iparams->user, group); +} + +int NCDModuleProcess_InitId (NCDModuleProcess *o, NCDModuleInst *n, NCD_string_id_t template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(template_name >= 0) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(handler_event) + + // init arguments + o->args = args; + o->handler_event = handler_event; + + // set no special functions + o->func_getspecialobj = NULL; + + // set state + set_process_state(o, PROCESS_STATE_INIT); + +#ifndef NDEBUG + // clear interp functions so we can assert they were set + o->interp_func_event = NULL; + o->interp_func_getobj = NULL; +#endif + + // init interpreter part + if (!(n->params->iparams->func_initprocess(n->params->iparams->user, o, template_name))) { + goto fail1; + } + + ASSERT(o->interp_func_event) + ASSERT(o->interp_func_getobj) + + // set state + set_process_state(o, PROCESS_STATE_DOWN); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + return 0; +} + +int NCDModuleProcess_InitValue (NCDModuleProcess *o, NCDModuleInst *n, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) +{ + DebugObject_Access(&n->d_obj); + ASSERT(n->state == STATE_DOWN_UNCLEAN || n->state == STATE_DOWN_CLEAN || + n->state == STATE_UP || + n->state == STATE_DYING) + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(handler_event) + + NCD_string_id_t template_name_id; + + if (NCDVal_IsIdString(template_name)) { + template_name_id = NCDVal_IdStringId(template_name); + } else { + NCDValContString cts; + if (!NCDVal_StringContinuize(template_name, &cts)) { + BLog(BLOG_ERROR, "NCDVal_StringContinuize failed"); + return 0; + } + + template_name_id = NCDStringIndex_GetBin(n->params->iparams->string_index, cts.data, NCDVal_StringLength(template_name)); + NCDValContString_Free(&cts); + if (template_name_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_GetBin failed"); + return 0; + } + } + + return NCDModuleProcess_InitId(o, n, template_name_id, args, handler_event); +} + +void NCDModuleProcess_Free (NCDModuleProcess *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_TERMINATED) +} + +void NCDModuleProcess_AssertFree (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_TERMINATED) +} + +void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj) +{ + DebugObject_Access(&o->d_obj); + + o->func_getspecialobj = func_getspecialobj; +} + +void NCDModuleProcess_Continue (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_DOWN_WAITING) + + set_process_state(o, PROCESS_STATE_DOWN); + + o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_CONTINUE); +} + +void NCDModuleProcess_Terminate (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP || + o->state == PROCESS_STATE_DOWN_WAITING) + + set_process_state(o, PROCESS_STATE_TERMINATING); + + o->interp_func_event(o->interp_user, NCDMODULEPROCESS_INTERP_EVENT_TERMINATE); +} + +int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state != PROCESS_STATE_INIT) + ASSERT(o->state != PROCESS_STATE_TERMINATED) + ASSERT(out_object) + + int res = o->interp_func_getobj(o->interp_user, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +static void process_assert_interp (NCDModuleProcess *o) +{ + // assert that the interpreter knows about the object, and we're not in init + ASSERT(o->state == PROCESS_STATE_DOWN || o->state == PROCESS_STATE_UP || + o->state == PROCESS_STATE_DOWN_WAITING || o->state == PROCESS_STATE_TERMINATING) +} + +void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, + NCDModuleProcess_interp_func_event interp_func_event, + NCDModuleProcess_interp_func_getobj interp_func_getobj) +{ + ASSERT(o->state == PROCESS_STATE_INIT) + ASSERT(interp_func_event) + ASSERT(interp_func_getobj) + + o->interp_user = interp_user; + o->interp_func_event = interp_func_event; + o->interp_func_getobj = interp_func_getobj; +} + +void NCDModuleProcess_Interp_Up (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_DOWN) + + set_process_state(o, PROCESS_STATE_UP); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_UP); + return; +} + +void NCDModuleProcess_Interp_Down (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_UP) + + set_process_state(o, PROCESS_STATE_DOWN_WAITING); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_DOWN); + return; +} + +void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(o->state == PROCESS_STATE_TERMINATING) + + set_process_state(o, PROCESS_STATE_TERMINATED); + + o->handler_event(o, NCDMODULEPROCESS_EVENT_TERMINATED); + return; +} + +int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) +{ + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(out_object) + + if (!NCDVal_IsInvalid(o->args)) { + if (name == NCD_STRING_ARGS) { + *out_object = NCDObject_Build(-1, o, process_args_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (name >= NCD_STRING_ARG0 && name <= NCD_STRING_ARG19) { + int num = name - NCD_STRING_ARG0; + if (num < NCDVal_ListCount(o->args)) { + *out_object = NCDObject_BuildFull(-1, o, num, NULL, process_arg_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } + } + + if (!o->func_getspecialobj) { + return 0; + } + + int res = o->func_getspecialobj(o, name, out_object); + ASSERT(res == 0 || res == 1) + + return res; +} + +static int process_args_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleProcess *o = NCDObject_DataPtr(obj); + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(!NCDVal_IsInvalid(o->args)) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out_value = NCDVal_NewCopy(mem, o->args); + if (NCDVal_IsInvalid(*out_value)) { + BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDVal_NewCopy failed"); + } + return 1; +} + +static int process_arg_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + NCDModuleProcess *o = NCDObject_DataPtr(obj); + DebugObject_Access(&o->d_obj); + process_assert_interp(o); + ASSERT(!NCDVal_IsInvalid(o->args)) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out_value = NCDVal_NewCopy(mem, NCDVal_ListGet(o->args, NCDObject_DataInt(obj))); + if (NCDVal_IsInvalid(*out_value)) { + BLog_LogToChannel(BLOG_CHANNEL_NCDModuleProcess, BLOG_ERROR, "NCDVal_NewCopy failed"); + } + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDModule.h b/external/badvpn_dns/ncd/NCDModule.h new file mode 100644 index 00000000..c00fe904 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDModule.h @@ -0,0 +1,1011 @@ +/** + * @file NCDModule.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDMODULE_H +#define BADVPN_NCD_NCDMODULE_H + +#include + +#include +#include +#include +#include +#include + +#ifndef BADVPN_NO_PROCESS +#include +#endif +#ifndef BADVPN_NO_UDEV +#include +#endif +#ifndef BADVPN_NO_RANDOM +#include +#endif + +#define NCDMODULE_EVENT_UP 1 +#define NCDMODULE_EVENT_DOWN 2 +#define NCDMODULE_EVENT_DOWNUP 3 +#define NCDMODULE_EVENT_DEAD 4 +#define NCDMODULE_EVENT_DEADERROR 5 + +struct NCDModuleInst_s; +struct NCDModuleProcess_s; +struct NCDModuleGroup; +struct NCDInterpModule; +struct NCDInterpModuleGroup; + +/** + * Function called to inform the interpeter of state changes of the + * module instance. + * Possible events are: + * + * - NCDMODULE_EVENT_UP: the instance came up. + * The instance was in down state. + * The instance enters up state. + * + * - NCDMODULE_EVENT_DOWN: the instance went down. + * The instance was in up state. + * The instance enters down state. + * + * After the instance goes down, the interpreter should eventually call + * {@link NCDModuleInst_Clean} or {@link NCDModuleInst_Die}, unless + * the module goes up again. + * + * - NCDMODULE_EVENT_DEAD: the module died. To determine if the module + * died with error, read the is_error member of {@link NCDModuleInst}. + * The instance enters dead state. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. The only exception is that it may free the + * instance from within the NCDMODULE_EVENT_DEAD event. + * + * @param inst the module instance + * @param event event number + */ +typedef void (*NCDModuleInst_func_event) (struct NCDModuleInst_s *inst, int event); + +/** + * Function called when the module instance wants the interpreter to + * resolve an object from the point of view of its statement. + * The instance will not be in dead state. + * This function must not have any side effects. + * + * @param inst the module instance + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_getobj) (struct NCDModuleInst_s *inst, NCD_string_id_t name, NCDObject *out_object); + +/** + * Function called when the module instance wants the interpreter to + * create a new process backend from a process template. + * The instance will not be in dead state. + * + * On success, the interpreter must have called {@link NCDModuleProcess_Interp_SetHandlers} + * from within this function, to allow communication with the controller of the process. + * On success, the new process backend enters down state. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param p handle for the new process backend + * @param template_name name of the template to create the process from, + * as an {@link NCDStringIndex} identifier + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_initprocess) (void *user, struct NCDModuleProcess_s *p, NCD_string_id_t template_name); + +/** + * Function called when the module instance wants the interpreter to + * initiate termination, as if it received an external terminatio request (signal). + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param exit_code exit code to return the the operating system. This overrides any previously + * set exit code, and will be overriden by a signal to the value 1. + * + */ +typedef void (*NCDModuleInst_func_interp_exit) (void *user, int exit_code); + +/** + * Function called when the module instance wants the interpreter to + * provide its extra command line arguments. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param mem value memory to use + * @param out_value write value reference here on success + * @return 1 if available, 0 if not available. If available, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModuleInst_func_interp_getargs) (void *user, NCDValMem *mem, NCDValRef *out_value); + +/** + * Function called when the module instance wants the interpreter to + * provide its retry time. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @return retry time in milliseconds + */ +typedef btime_t (*NCDModuleInst_func_interp_getretrytime) (void *user); + +/** + * Function called when the module instance wants the interpreter to + * load a new module group. + * + * @param user value of 'user' member of {@link NCDModuleInst_iparams} + * @param group module group to load + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleInst_func_interp_loadgroup) (void *user, const struct NCDModuleGroup *group); + +#define NCDMODULEPROCESS_EVENT_UP 1 +#define NCDMODULEPROCESS_EVENT_DOWN 2 +#define NCDMODULEPROCESS_EVENT_TERMINATED 3 + +/** + * Handler which reports process state changes from the interpreter. + * Possible events are: + * + * - NCDMODULEPROCESS_EVENT_UP: the process went up. + * The process was in down state. + * The process enters up state. + * + * - NCDMODULEPROCESS_EVENT_DOWN: the process went down. + * The process was in up state. + * The process enters waiting state. + * + * NOTE: the process enters waiting state, NOT down state, and is paused. + * To allow the process to continue, call {@link NCDModuleProcess_Continue}. + * + * - NCDMODULEPROCESS_EVENT_TERMINATED: the process terminated. + * The process was in terminating state. + * The process enters terminated state. + * + * @param user pointer to the process. Use {@link UPPER_OBJECT} to retrieve the pointer + * to the containing struct. + * @param event event number + */ +typedef void (*NCDModuleProcess_handler_event) (struct NCDModuleProcess_s *process, int event); + +/** + * Function called when the interpreter wants to resolve a special + * object in the process. + * This function must have no side effects. + * + * @param user pointer to the process. Use {@link UPPER_OBJECT} to retrieve the pointer + * to the containing struct. + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModuleProcess_func_getspecialobj) (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object); + +#define NCDMODULEPROCESS_INTERP_EVENT_CONTINUE 1 +#define NCDMODULEPROCESS_INTERP_EVENT_TERMINATE 2 + +/** + * Function called to report process backend requests to the interpreter. + * Possible events are: + * + * - NCDMODULEPROCESS_INTERP_EVENT_CONTINUE: the process can continue. + * The process backend was in waiting state. + * The process backend enters down state. + * + * - NCDMODULEPROCESS_INTERP_EVENT_TERMINATE: the process should terminate. + * The process backend was in down, up or waiting state. + * The process backend enters terminating state. + * + * The interpreter should call {@link NCDModuleProcess_Interp_Terminated} + * when the process terminates. + * + * This function is not being called in event context. The interpreter should + * only update its internal state, and visibly react only via jobs that it pushes + * from within this function. + * + * @param user as in {@link NCDModuleProcess_Interp_SetHandlers} + * @param event event number + */ +typedef void (*NCDModuleProcess_interp_func_event) (void *user, int event); + +/** + * Function called to have the interpreter resolve an object within the process + * of a process backend. + * This function must not have any side effects. + * + * @param user as in {@link NCDModuleProcess_Interp_SetHandlers} + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 in failure + */ +typedef int (*NCDModuleProcess_interp_func_getobj) (void *user, NCD_string_id_t name, NCDObject *out_object); + +struct NCDModule; + +/** + * Contains parameters to the module initialization function + * ({@link NCDModule_func_new2}) that are passed indirectly. + */ +struct NCDModuleInst_new_params { + /** + * A reference to the argument list for the module instance. + * The reference remains valid as long as the backend instance + * exists. Unless the module has the NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + * flag set, it is guaranteed that any strings within the arguments will be + * some kind of ContinuousString. + */ + NCDValRef args; + + /** + * If the module instance corresponds to a method-like statement, + * this pointer identifies the object it is being invoked with. + * If the object is a statement (i.e. a {@link NCDModuleInst}), then this + * will be the NCDModuleInst pointer, and {@link NCDModuleInst_Backend_GetUser} + * can be called on this to retrieve the pointer to preallocated memory for + * the backend instance; *unless* {@link NCDModuleInst_Backend_PassMemToMethods} + * was called for the object on which the method is being called, in which case + * this will directly point to the preallocated memory. + * On the other hand, if this is a method on an internal object built using + * only {@link NCDObject_Build} or {@link NCDObject_BuildFull}, + * this pointer will be whatever was passed as the "data_ptr" argument, for the + * first function, and as "method_user", for the latter function. + */ + void *method_user; +}; + +/** + * Contains parameters to {@link NCDModuleInst_Init} that are passed indirectly. + * This itself only contains parameters related to communication between the + * backend and the creator of the module instance; other parameters are passed + * via the iparams member; + */ +struct NCDModuleInst_params { + /** + * Callback to report state changes. + */ + NCDModuleInst_func_event func_event; + /** + * Callback to resolve objects from the viewpoint of the instance. + */ + NCDModuleInst_func_getobj func_getobj; + /** + * Log function which appends a log prefix with {@link BLog_Append}. + */ + BLog_logfunc logfunc; + /** + * Pointer to an {@link NCDModuleInst_iparams} structure, which exposes + * services provided by the interpreter. + */ + const struct NCDModuleInst_iparams *iparams; +}; + +/** + * Contains parameters to {@link NCDModuleInst_Init} that are passed indirectly. + * This only contains parameters related to services provided by the interpreter. + */ +struct NCDModuleInst_iparams { + /** + * Reactor we live in. + */ + BReactor *reactor; +#ifndef BADVPN_NO_PROCESS + /** + * Process manager. + */ + BProcessManager *manager; +#endif +#ifndef BADVPN_NO_UDEV + /** + * Udev manager. + */ + NCDUdevManager *umanager; +#endif +#ifndef BADVPN_NO_RANDOM + /** + * Random number generator. + */ + BRandom2 *random2; +#endif + /** + * String index which keeps a mapping between strings and string identifiers. + */ + NCDStringIndex *string_index; + /** + * Pointer passed to the interpreter callbacks below, for state keeping. + */ + void *user; + /** + * Callback to create a new template process. + */ + NCDModuleInst_func_initprocess func_initprocess; + /** + * Callback to request interpreter termination. + */ + NCDModuleInst_func_interp_exit func_interp_exit; + /** + * Callback to get extra command line arguments. + */ + NCDModuleInst_func_interp_getargs func_interp_getargs; + /** + * Callback to get retry time. + */ + NCDModuleInst_func_interp_getretrytime func_interp_getretrytime; + /** + * Callback to load a module group. + */ + NCDModuleInst_func_interp_loadgroup func_loadgroup; +}; + +/** + * Module instance. + * The module instance is initialized by the interpreter by calling + * {@link NCDModuleInst_Init}. It is implemented by a module backend + * specified in a {@link NCDModule}. + */ +typedef struct NCDModuleInst_s { + const struct NCDInterpModule *m; + const struct NCDModuleInst_params *params; + void *mem; // not modified by NCDModuleInst (but passed to module) + unsigned int state:3; + unsigned int pass_mem_to_methods:1; + unsigned int istate:3; // untouched by NCDModuleInst + DebugObject d_obj; +} NCDModuleInst; + +/** + * Process created from a process template on behalf of a module backend + * instance, implemented by the interpreter. + */ +typedef struct NCDModuleProcess_s { + NCDValRef args; + NCDModuleProcess_handler_event handler_event; + NCDModuleProcess_func_getspecialobj func_getspecialobj; + void *interp_user; + NCDModuleProcess_interp_func_event interp_func_event; + NCDModuleProcess_interp_func_getobj interp_func_getobj; +#ifndef NDEBUG + int state; +#endif + DebugObject d_obj; +} NCDModuleProcess; + +/** + * Initializes an instance of an NCD module. + * The instance is initialized in down state. + * WARNING: this directly calls the module backend; expect to be called back + * + * This and other non-Backend methods are the interpreter interface. + * The Backend methods are the module backend interface and are documented + * independently with their own logical states. + * + * NOTE: the instance structure \a n should have the member 'mem' initialized + * to point to preallocated memory for the statement. This memory must be + * at least m->prealloc_size big and must be properly aligned for any object. + * The 'mem' pointer is never modified by NCDModuleInst, so that the interpreter + * can use it as outside the lifetime of NCDModuleInst. + * + * @param n the instance + * @param m pointer to the {@link NCDInterpModule} structure representing the module + * to be instantiated + * @param method_context a context pointer passed to the module backend, applicable to method-like + * statements only. This should be set to the 'user' member of the + * {@link NCDObject} which represents the base object for the method. + * The caller must ensure that the NCDObject that was used is of the type + * expected by the module being instanciated. + * @param args arguments to the module. Must be a list value. Must be available and unchanged + * as long as the instance exists. Unless the module has the + * NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS flag set, any strings within the + * arguments must be some kind of ContinuousString. This can be ensured by calling + * {@link NCDValMem_ConvertNonContinuousStrings}. + * @param user argument to callback functions + * @param params more parameters, see {@link NCDModuleInst_params} + */ +void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDInterpModule *m, void *method_context, NCDValRef args, const struct NCDModuleInst_params *params); + +/** + * Frees the instance. + * The instance must be in dead state. + * + * @param n the instance + */ +void NCDModuleInst_Free (NCDModuleInst *n); + +/** + * Requests the instance to die. + * The instance must be in down or up state. + * The instance enters dying state. + * WARNING: this directly calls the module backend; expect to be called back + * + * @param n the instance + */ +void NCDModuleInst_Die (NCDModuleInst *n); + +/** + * Attempts to destroy the instance immediately. + * This function can be used to optimize destroying instances of modules which + * don't specify any {@link NCDModule_func_die} handler. If immediate destruction + * is not possible, this does nothing and returns 0; {@link NCDModuleInst_Die} + * should be used to destroy the instance instead. If however immediate destruction + * is possible, this destroys the module instance and returns 1; {@link NCDModuleInst_Free} + * must not be called after that. + * The instance must be in down or up state, as for {@link NCDModuleInst_Die}. + * + * @param n the instance + * @return 1 if destruction was performed, 0 if not + */ +int NCDModuleInst_TryFree (NCDModuleInst *n); + +/** + * Informs the module that it is in a clean state to proceed. + * The instance must be in down state. + * WARNING: this directly calls the module backend; expect to be called back + * + * @param n the instance + */ +void NCDModuleInst_Clean (NCDModuleInst *n); + +/** + * Returns an {@link NCDObject} which can be used to resolve variables and objects + * within this instance, as well as call its methods. The resulting object may only + * be used immediately, and becomes invalid when the instance is freed. + * + * @param n the instance + * @return an NCDObject for this instance + */ +NCDObject NCDModuleInst_Object (NCDModuleInst *n); + +/** + * If this is called, any methods called on this object will receive the preallocated + * memory pointer as the object state pointer. This means that in the + * {@link NCDModule_func_getvar2} function which is called when a method is created, + * the preallocated memory should be accessed as params->method_user. + * By default, however, params->method_user points to the NCDModuleInst of the base + * object, and {@link NCDModuleInst_Backend_GetUser} is needed to retrieve the + * preallocated memory pointer. + */ +void NCDModuleInst_Backend_PassMemToMethods (NCDModuleInst *n); + +/** + * Retuns the state pointer passed to handlers of a module backend instance; + * see {@link NCDModule_func_new2}. + * + * @param n backend instance handle + * @return argument passed to handlers + */ +void * NCDModuleInst_Backend_GetUser (NCDModuleInst *n); + +/** + * Puts the backend instance into up state. + * The instance must be in down state. + * The instance enters up state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Up (NCDModuleInst *n); + +/** + * Puts the backend instance into down state. + * The instance must be in up state. + * The instance enters down state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Down (NCDModuleInst *n); + +/** + * Puts the backend instance into down state, then immediatly back into the up state. + * This effectively causes the interpreter to start backtracking to this statement. + * The instance must be in up state, and remains in up state. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_DownUp (NCDModuleInst *n); + +/** + * Destroys the backend instance. + * The backend instance handle becomes invalid and must not be used from + * the backend any longer. + * + * @param n backend instance handle + */ +void NCDModuleInst_Backend_Dead (NCDModuleInst *n); + +/** + * Like {@link NCDModuleInst_Backend_Dead}, but also reports an error condition + * to the interpreter. + */ +void NCDModuleInst_Backend_DeadError (NCDModuleInst *n); + +/** + * Resolves an object for a backend instance, from the point of the instance's + * statement in the containing process. + * + * @param n backend instance handle + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleInst_Backend_GetObj (NCDModuleInst *n, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Logs a backend instance message. + * + * @param n backend instance handle + * @param channel log channel + * @param level loglevel + * @param fmt format string as in printf, arguments follow + */ +void NCDModuleInst_Backend_Log (NCDModuleInst *n, int channel, int level, const char *fmt, ...); + +/** + * Like {@link NCDModuleInst_Backend_Log}, but the extra arguments are passed + * as a va_list. This allows creation of logging wrappers. + */ +void NCDModuleInst_Backend_LogVarArg (NCDModuleInst *n, int channel, int level, const char *fmt, va_list vl); + +/** + * Returns a logging context. The context is valid until the backend dies. + */ +BLogContext NCDModuleInst_Backend_LogContext (NCDModuleInst *n); + +/** + * Initiates interpreter termination. + * + * @param n backend instance handle + * @param exit_code exit code to return to the operating system. This overrides + * any previously set exit code, and will be overriden by a + * termination signal to the value 1. + */ +void NCDModuleInst_Backend_InterpExit (NCDModuleInst *n, int exit_code); + +/** + * Retrieves extra command line arguments passed to the interpreter. + * + * @param n backend instance handle + * @param mem value memory to use + * @param out_value the arguments will be written here on success as a list value + * @return 1 if available, 0 if not available. If available, but out of memory, returns 1 + * and an invalid value. + */ +int NCDModuleInst_Backend_InterpGetArgs (NCDModuleInst *n, NCDValMem *mem, NCDValRef *out_value); + +/** + * Returns the retry time of the intepreter. + * + * @param n backend instance handle + * @return retry time in milliseconds + */ +btime_t NCDModuleInst_Backend_InterpGetRetryTime (NCDModuleInst *n); + +/** + * Loads a module group into the interpreter. + * + * @param n backend instance handle + * @param group module group to load + * @return 1 on success, 0 on failure + */ +int NCDModuleInst_Backend_InterpLoadGroup (NCDModuleInst *n, const struct NCDModuleGroup *group); + +/** + * Initializes a process in the interpreter from a process template. + * This must be called on behalf of a module backend instance. + * The process is initializes in down state. + * + * @param o the process + * @param n backend instance whose interpreter will be providing the process + * @param template_name name of the process template as an {@link NCDStringIndex} identifier + * @param args arguments to the process. Must be an invalid value or a list value. + * The value must be available and unchanged while the process exists. + * @param handler_event handler which reports events about the process from the + * interpreter + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_InitId (NCDModuleProcess *o, NCDModuleInst *n, NCD_string_id_t template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) WARN_UNUSED; + +/** + * Wrapper around {@link NCDModuleProcess_InitId} which takes the template name as an + * {@link NCDValRef}, which must point to a string value. + */ +int NCDModuleProcess_InitValue (NCDModuleProcess *o, NCDModuleInst *n, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler_event) WARN_UNUSED; + +/** + * Frees the process. + * The process must be in terminated state. + * + * @param o the process + */ +void NCDModuleProcess_Free (NCDModuleProcess *o); + +/** + * Does nothing. + * The process must be in terminated state. + * + * @param o the process + */ +void NCDModuleProcess_AssertFree (NCDModuleProcess *o); + +/** + * Sets callback functions for providing special objects within the process. + * + * @param o the process + * @param func_getspecialobj function for resolving special objects, or NULL + */ +void NCDModuleProcess_SetSpecialFuncs (NCDModuleProcess *o, NCDModuleProcess_func_getspecialobj func_getspecialobj); + +/** + * Continues the process after the process went down. + * The process must be in waiting state. + * The process enters down state. + * + * @param o the process + */ +void NCDModuleProcess_Continue (NCDModuleProcess *o); + +/** + * Requests the process to terminate. + * The process must be in down, up or waiting state. + * The process enters terminating state. + * + * @param o the process + */ +void NCDModuleProcess_Terminate (NCDModuleProcess *o); + +/** + * Resolves an object within the process from the point + * at the end of the process. + * This function has no side effects. + * + * @param o the process + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_GetObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Sets callback functions for the interpreter to implement the + * process backend. + * Must be called from within {@link NCDModuleInst_func_initprocess} + * if success is to be reported there. + * + * @param o process backend handle, as in {@link NCDModuleInst_func_initprocess} + * @param interp_user argument to callback functions + * @param interp_func_event function for reporting continue/terminate requests + * @param interp_func_getobj function for resolving objects within the process + */ +void NCDModuleProcess_Interp_SetHandlers (NCDModuleProcess *o, void *interp_user, + NCDModuleProcess_interp_func_event interp_func_event, + NCDModuleProcess_interp_func_getobj interp_func_getobj); + +/** + * Reports the process backend as up. + * The process backend must be in down state. + * The process backend enters up state. + * WARNING: this directly calls the process creator; expect to be called back + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Up (NCDModuleProcess *o); + +/** + * Reports the process backend as down. + * The process backend must be in up state. + * The process backend enters waiting state. + * WARNING: this directly calls the process creator; expect to be called back + * + * NOTE: the backend enters waiting state, NOT down state. The interpreter should + * pause the process until {@link NCDModuleProcess_interp_func_event} reports + * NCDMODULEPROCESS_INTERP_EVENT_CONTINUE, unless termination is requested via + * NCDMODULEPROCESS_INTERP_EVENT_TERMINATE. + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Down (NCDModuleProcess *o); + +/** + * Reports termination of the process backend. + * The process backend must be in terminating state. + * The process backend handle becomes invalid and must not be used + * by the interpreter any longer. + * WARNING: this directly calls the process creator; expect to be called back + * + * @param o process backend handle + */ +void NCDModuleProcess_Interp_Terminated (NCDModuleProcess *o); + +/** + * Resolves a special process object for the process backend. + * + * @param o process backend handle + * @param name name of the object as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +int NCDModuleProcess_Interp_GetSpecialObj (NCDModuleProcess *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Function called before any instance of any backend in a module + * group is created; + * + * @param params structure containing global resources, such as + * {@link BReactor}, {@link BProcessManager} and {@link NCDUdevManager}. + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModule_func_globalinit) (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params); + +/** + * Function called to clean up after {@link NCDModule_func_globalinit} and modules + * in a module group. + * There are no backend instances alive from this module group. + */ +typedef void (*NCDModule_func_globalfree) (struct NCDInterpModuleGroup *group); + +/** + * Handler called to create an new module backend instance. + * The backend is initialized in down state. + * + * If the backend fails initialization, this function should report the backend + * instance to have died with error by calling {@link NCDModuleInst_Backend_DeadError}. + * + * @param o if the module specifies a positive alloc_size value in the {@link NCDModule} + * structure, this will point to the allocated memory that can be used by the + * module instance while it exists. If the alloc_size is 0 (default), this may or + * may not be NULL. + * @param i module backend instance handler. The backend may only use this handle via + * the Backend functions of {@link NCDModuleInst}. + */ +typedef void (*NCDModule_func_new2) (void *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params); + +/** + * Handler called to request termination of a backend instance. + * The backend instance was in down or up state. + * The backend instance enters dying state. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + */ +typedef void (*NCDModule_func_die) (void *o); + +/** + * Function called to resolve a variable within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the variable to resolve + * @param mem value memory to use + * @param out on success, the backend should initialize the value here + * @return 1 if exists, 0 if not exists. If exists, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValMem *mem, NCDValRef *out); + +/** + * Function called to resolve a variable within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the variable to resolve as an {@link NCDStringIndex} identifier + * @param mem value memory to use + * @param out on success, the backend should initialize the value here + * @return 1 if exists, 0 if not exists. If exists, but out of memory, return 1 + * and an invalid value. + */ +typedef int (*NCDModule_func_getvar2) (void *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); + +/** + * Function called to resolve an object within a backend instance. + * The backend instance is in up state, or in up or down state if can_resolve_when_down=1. + * This function must not have any side effects. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + * @param name name of the object to resolve as an {@link NCDStringIndex} identifier + * @param out_object the object will be returned here + * @return 1 on success, 0 on failure + */ +typedef int (*NCDModule_func_getobj) (void *o, NCD_string_id_t name, NCDObject *out_object); + +/** + * Handler called when the module instance is in a clean state. + * This means that all statements preceding it in the process are + * up, this statement is down, and all following statements are + * uninitialized. When a backend instance goes down, it is guaranteed, + * as long as it stays down, that either this will be called or + * termination will be requested with {@link NCDModule_func_die}. + * The backend instance was in down state. + * + * @param o state pointer, as in {@link NCDModule_func_new2} + */ +typedef void (*NCDModule_func_clean) (void *o); + +#define NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN (1 << 0) +#define NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS (1 << 1) + +/** + * Structure encapsulating the implementation of a module backend. + */ +struct NCDModule { + /** + * If this implements a plain statement, the name of the statement. + * If this implements a method, then "base_type::method_name". + */ + const char *type; + + /** + * The base type for methods operating on instances of this backend. + * Any module with type of form "base_type::method_name" is considered + * a method of instances of this backend. + * If this is NULL, the base type will default to type. + */ + const char *base_type; + + /** + * Function called to create an new backend instance. + */ + NCDModule_func_new2 func_new2; + + /** + * Function called to request termination of a backend instance. + * May be NULL, in which case the default is to call NCDModuleInst_Backend_Dead(). + */ + NCDModule_func_die func_die; + + /** + * Function called to resolve a variable within the backend instance. + * May be NULL. + */ + NCDModule_func_getvar func_getvar; + + /** + * Function called to resolve a variable within the backend instance. + * May be NULL. + */ + NCDModule_func_getvar2 func_getvar2; + + /** + * Function called to resolve an object within the backend instance. + * May be NULL. + */ + NCDModule_func_getobj func_getobj; + + /** + * Function called when the backend instance is in a clean state. + * May be NULL. + */ + NCDModule_func_clean func_clean; + + /** + * Various flags. + * + * - NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN + * Whether the interpreter is allowed to call func_getvar and func_getobj + * even when the backend instance is in down state (as opposed to just + * in up state. + * + * - NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + * If not set, strings within arguments which are not some kind of ContinuousString + * will be converted to some kind of ContinuousString before the module's init + * function is called. If set, they will not be, and the module must work with any + * kind of strings (i.e. {@link NCDVal_StringData} may not be allowed). + */ + int flags; + + /** + * The amount of memory to preallocate for each module instance. + * Preallocation can be used to avoid having to allocate memory from + * module initialization. The memory can be accessed via the first + * argument to {@link NCDModule_func_new2} and other calls. + */ + int alloc_size; +}; + +/** + * Structure encapsulating a group of module backend implementations, + * with global init and free functions. + */ +struct NCDModuleGroup { + /** + * Function called before any instance of any module backend in this + * group is crated. May be NULL. + */ + NCDModule_func_globalinit func_globalinit; + + /** + * Function called to clean up after {@link NCDModule_func_globalinit}. + * May be NULL. + */ + NCDModule_func_globalfree func_globalfree; + + /** + * Array of module backends. The array must be terminated with a + * structure that has a NULL type member. + */ + const struct NCDModule *modules; + + /** + * A pointer to an array of strings which will be mapped to + * {@link NCDStringIndex} string identifiers for the module to use. + * The array must be terminated by NULL. The resulting string + * identifiers will be available in the 'strings' member in + * {@link NCDInterpModuleGroup}. + */ + const char *const*strings; +}; + +/** + * Represents an {@link NCDModule} within an interpreter. + * This structure is initialized by the interpreter when it loads a module group. + */ +struct NCDInterpModule { + /** + * A copy of the original NCDModule structure, + */ + struct NCDModule module; + + /** + * The string identifier of this module's base_type (or type if base_type is + * not specified) according to {@link NCDStringIndex}. + */ + NCD_string_id_t base_type_id; + + /** + * A pointer to the {@link NCDInterpModuleGroup} representing the group + * this module belongs to. + */ + struct NCDInterpModuleGroup *group; +}; + +/** + * Represents an {@link NCDModuleGroup} within an interpreter. + * This structure is initialized by the interpreter when it loads a module group. + */ +struct NCDInterpModuleGroup { + /** + * A copy of the original NCDModuleGroup structure. + */ + struct NCDModuleGroup group; + + /** + * An array of string identifiers corresponding to the strings + * in the 'strings' member of NCDModuleGroup. May be NULL if there + * are no strings in the NCDModuleGroup. + */ + NCD_string_id_t *strings; + + /** + * Pointer which allows the module to keep private interpreter-wide state. + * This can be freely modified by the module; the interpeter will not + * read or write it. + */ + void *group_state; +}; + +#endif diff --git a/external/badvpn_dns/ncd/NCDModuleIndex.c b/external/badvpn_dns/ncd/NCDModuleIndex.c new file mode 100644 index 00000000..12ef48a6 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDModuleIndex.c @@ -0,0 +1,372 @@ +/** + * @file NCDModuleIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "NCDModuleIndex.h" + +#include + +#include "NCDModuleIndex_mhash.h" +#include + +static int string_pointer_comparator (void *user, void *v1, void *v2) +{ + const char **s1 = v1; + const char **s2 = v2; + int cmp = strcmp(*s1, *s2); + return B_COMPARE(cmp, 0); +} + +static struct NCDModuleIndex_module * find_module (NCDModuleIndex *o, const char *type) +{ + NCDModuleIndex__MHashRef ref = NCDModuleIndex__MHash_Lookup(&o->modules_hash, 0, type); + ASSERT(!ref.link || !strcmp(ref.link->imodule.module.type, type)) + return ref.link; +} + +#ifndef NDEBUG +static struct NCDModuleIndex_base_type * find_base_type (NCDModuleIndex *o, const char *base_type) +{ + BAVLNode *node = BAVL_LookupExact(&o->base_types_tree, &base_type); + if (!node) { + return NULL; + } + + struct NCDModuleIndex_base_type *bt = UPPER_OBJECT(node, struct NCDModuleIndex_base_type, base_types_tree_node); + ASSERT(!strcmp(bt->base_type, base_type)) + + return bt; +} +#endif + +static int add_method (const char *type, const struct NCDInterpModule *module, NCDMethodIndex *method_index, int *out_method_id) +{ + ASSERT(type) + ASSERT(module) + ASSERT(method_index) + ASSERT(out_method_id) + + const char search[] = "::"; + size_t search_len = sizeof(search) - 1; + + size_t table[sizeof(search) - 1]; + build_substring_backtrack_table_reverse(search, search_len, table); + + size_t pos; + if (!find_substring_reverse(type, strlen(type), search, search_len, table, &pos)) { + *out_method_id = -1; + return 1; + } + + ASSERT(pos >= 0) + ASSERT(pos <= strlen(type) - search_len) + ASSERT(!memcmp(type + pos, search, search_len)) + + int method_id = NCDMethodIndex_AddMethod(method_index, type, pos, type + pos + search_len, module); + if (method_id < 0) { + BLog(BLOG_ERROR, "NCDMethodIndex_AddMethod failed"); + return 0; + } + + *out_method_id = method_id; + return 1; +} + +int NCDModuleIndex_Init (NCDModuleIndex *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + // init modules hash + if (!NCDModuleIndex__MHash_Init(&o->modules_hash, NCDMODULEINDEX_MODULES_HASH_SIZE)) { + BLog(BLOG_ERROR, "NCDModuleIndex__MHash_Init failed"); + goto fail0; + } + +#ifndef NDEBUG + // init base types tree + BAVL_Init(&o->base_types_tree, OFFSET_DIFF(struct NCDModuleIndex_base_type, base_type, base_types_tree_node), string_pointer_comparator, NULL); +#endif + + // init groups list + LinkedList0_Init(&o->groups_list); + + // init method index + if (!NCDMethodIndex_Init(&o->method_index, string_index)) { + BLog(BLOG_ERROR, "NCDMethodIndex_Init failed"); + goto fail1; + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + NCDModuleIndex__MHash_Free(&o->modules_hash); +fail0: + return 0; +} + +void NCDModuleIndex_Free (NCDModuleIndex *o) +{ + DebugObject_Free(&o->d_obj); + + // free groups + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->groups_list)) { + struct NCDModuleIndex_group *ig = UPPER_OBJECT(ln, struct NCDModuleIndex_group, groups_list_node); + if (ig->igroup.group.func_globalfree) { + ig->igroup.group.func_globalfree(&ig->igroup); + } + BFree(ig->igroup.strings); + LinkedList0_Remove(&o->groups_list, &ig->groups_list_node); + BFree(ig); + } + +#ifndef NDEBUG + // free base types + BAVLNode *tn; + while (tn = BAVL_GetFirst(&o->base_types_tree)) { + struct NCDModuleIndex_base_type *bt = UPPER_OBJECT(tn, struct NCDModuleIndex_base_type, base_types_tree_node); + BAVL_Remove(&o->base_types_tree, &bt->base_types_tree_node); + BFree(bt); + } +#endif + + // free method index + NCDMethodIndex_Free(&o->method_index); + + // free modules hash + NCDModuleIndex__MHash_Free(&o->modules_hash); +} + +int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, const struct NCDModuleInst_iparams *iparams, NCDStringIndex *string_index) +{ + DebugObject_Access(&o->d_obj); + ASSERT(group) + ASSERT(iparams) + ASSERT(string_index) + + // count modules in the group + size_t num_modules = 0; + while (group->modules[num_modules].type) { + num_modules++; + } + + // compute allocation size + bsize_t size = bsize_add(bsize_fromsize(sizeof(struct NCDModuleIndex_group)), bsize_mul(bsize_fromsize(num_modules), bsize_fromsize(sizeof(struct NCDModuleIndex_module)))); + + // allocate group + struct NCDModuleIndex_group *ig = BAllocSize(size); + if (!ig) { + BLog(BLOG_ERROR, "BAllocSize failed"); + goto fail0; + } + + // insert to groups list + LinkedList0_Prepend(&o->groups_list, &ig->groups_list_node); + + // copy NCDModuleGroup + ig->igroup.group = *group; + + if (!group->strings) { + // not resolving strings + ig->igroup.strings = NULL; + } else { + // compute number of strings + size_t num_strings = 0; + while (group->strings[num_strings]) { + num_strings++; + } + + // allocate array for string IDs + ig->igroup.strings = BAllocArray(num_strings, sizeof(ig->igroup.strings[0])); + if (!ig->igroup.strings) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + // map strings to IDs + for (size_t i = 0; i < num_strings; i++) { + ig->igroup.strings[i] = NCDStringIndex_Get(string_index, group->strings[i]); + if (ig->igroup.strings[i] < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto fail2; + } + } + } + + // call group init function + if (group->func_globalinit) { + if (!group->func_globalinit(&ig->igroup, iparams)) { + BLog(BLOG_ERROR, "func_globalinit failed"); + goto fail2; + } + } + + size_t num_inited_modules = 0; + + // initialize modules + for (size_t i = 0; i < num_modules; i++) { + const struct NCDModule *nm = &group->modules[i]; + struct NCDModuleIndex_module *m = &ig->modules[i]; + + // make sure a module with this name doesn't exist already + if (find_module(o, nm->type)) { + BLog(BLOG_ERROR, "module type '%s' already exists", nm->type); + goto loop_fail0; + } + + // copy NCDModule structure + m->imodule.module = *nm; + + // determine base type + const char *base_type = (nm->base_type ? nm->base_type : nm->type); + ASSERT(base_type) + + // map base type to ID + m->imodule.base_type_id = NCDStringIndex_Get(string_index, base_type); + if (m->imodule.base_type_id < 0) { + BLog(BLOG_ERROR, "NCDStringIndex_Get failed"); + goto loop_fail0; + } + + // set group pointer + m->imodule.group = &ig->igroup; + + // register method + if (!add_method(nm->type, &m->imodule, &o->method_index, &m->method_id)) { + goto loop_fail0; + } + +#ifndef NDEBUG + // ensure that this base_type does not appear in any other groups + struct NCDModuleIndex_base_type *bt = find_base_type(o, base_type); + if (bt) { + if (bt->group != ig) { + BLog(BLOG_ERROR, "module base type '%s' already exists in another module group", base_type); + goto loop_fail1; + } + } else { + if (!(bt = BAlloc(sizeof(*bt)))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto loop_fail1; + } + bt->base_type = base_type; + bt->group = ig; + ASSERT_EXECUTE(BAVL_Insert(&o->base_types_tree, &bt->base_types_tree_node, NULL)) + } +#endif + + // insert to modules hash + NCDModuleIndex__MHashRef ref = {m, m}; + int res = NCDModuleIndex__MHash_Insert(&o->modules_hash, 0, ref, NULL); + ASSERT_EXECUTE(res) + + num_inited_modules++; + continue; + +#ifndef NDEBUG + loop_fail1: + if (m->method_id >= 0) { + NCDMethodIndex_RemoveMethod(&o->method_index, m->method_id); + } +#endif + loop_fail0: + goto fail3; + } + + return 1; + +fail3: + while (num_inited_modules-- > 0) { + struct NCDModuleIndex_module *m = &ig->modules[num_inited_modules]; + NCDModuleIndex__MHashRef ref = {m, m}; + NCDModuleIndex__MHash_Remove(&o->modules_hash, 0, ref); +#ifndef NDEBUG + const struct NCDModule *nm = &group->modules[num_inited_modules]; + const char *base_type = (nm->base_type ? nm->base_type : nm->type); + struct NCDModuleIndex_base_type *bt = find_base_type(o, base_type); + if (bt) { + ASSERT(bt->group == ig) + BAVL_Remove(&o->base_types_tree, &bt->base_types_tree_node); + BFree(bt); + } +#endif + if (m->method_id >= 0) { + NCDMethodIndex_RemoveMethod(&o->method_index, m->method_id); + } + } + if (group->func_globalfree) { + group->func_globalfree(&ig->igroup); + } +fail2: + BFree(ig->igroup.strings); +fail1: + LinkedList0_Remove(&o->groups_list, &ig->groups_list_node); + BFree(ig); +fail0: + return 0; +} + +const struct NCDInterpModule * NCDModuleIndex_FindModule (NCDModuleIndex *o, const char *type) +{ + DebugObject_Access(&o->d_obj); + ASSERT(type) + + struct NCDModuleIndex_module *m = find_module(o, type); + if (!m) { + return NULL; + } + + return &m->imodule; +} + +int NCDModuleIndex_GetMethodNameId (NCDModuleIndex *o, const char *method_name) +{ + DebugObject_Access(&o->d_obj); + ASSERT(method_name) + + return NCDMethodIndex_GetMethodNameId(&o->method_index, method_name); +} + +const struct NCDInterpModule * NCDModuleIndex_GetMethodModule (NCDModuleIndex *o, NCD_string_id_t obj_type, int method_name_id) +{ + DebugObject_Access(&o->d_obj); + + return NCDMethodIndex_GetMethodModule(&o->method_index, obj_type, method_name_id); +} diff --git a/external/badvpn_dns/ncd/NCDModuleIndex.h b/external/badvpn_dns/ncd/NCDModuleIndex.h new file mode 100644 index 00000000..f7cc2559 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDModuleIndex.h @@ -0,0 +1,86 @@ +/** + * @file NCDModuleIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDMODULEINDEX_H +#define BADVPN_NCDMODULEINDEX_H + +#include +#include +#include +#include +#include +#include +#include + +#define NCDMODULEINDEX_MODULES_HASH_SIZE 512 + +struct NCDModuleIndex_module { + struct NCDInterpModule imodule; + struct NCDModuleIndex_module *hash_next; + int method_id; +}; + +#ifndef NDEBUG +struct NCDModuleIndex_base_type { + const char *base_type; + struct NCDModuleIndex_group *group; + BAVLNode base_types_tree_node; +}; +#endif + +struct NCDModuleIndex_group { + LinkedList0Node groups_list_node; + struct NCDInterpModuleGroup igroup; + struct NCDModuleIndex_module modules[]; +}; + +typedef struct NCDModuleIndex_module *NCDModuleIndex__mhash_link; +typedef const char *NCDModuleIndex__mhash_key; + +#include "NCDModuleIndex_mhash.h" +#include + +typedef struct { + NCDModuleIndex__MHash modules_hash; +#ifndef NDEBUG + BAVL base_types_tree; +#endif + LinkedList0 groups_list; + NCDMethodIndex method_index; + DebugObject d_obj; +} NCDModuleIndex; + +int NCDModuleIndex_Init (NCDModuleIndex *o, NCDStringIndex *string_index) WARN_UNUSED; +void NCDModuleIndex_Free (NCDModuleIndex *o); +int NCDModuleIndex_AddGroup (NCDModuleIndex *o, const struct NCDModuleGroup *group, const struct NCDModuleInst_iparams *iparams, NCDStringIndex *string_index) WARN_UNUSED; +const struct NCDInterpModule * NCDModuleIndex_FindModule (NCDModuleIndex *o, const char *type); +int NCDModuleIndex_GetMethodNameId (NCDModuleIndex *o, const char *method_name); +const struct NCDInterpModule * NCDModuleIndex_GetMethodModule (NCDModuleIndex *o, NCD_string_id_t obj_type, int method_name_id); + +#endif diff --git a/external/badvpn_dns/ncd/NCDModuleIndex_mhash.h b/external/badvpn_dns/ncd/NCDModuleIndex_mhash.h new file mode 100644 index 00000000..8057b39a --- /dev/null +++ b/external/badvpn_dns/ncd/NCDModuleIndex_mhash.h @@ -0,0 +1,12 @@ +#define CHASH_PARAM_NAME NCDModuleIndex__MHash +#define CHASH_PARAM_ENTRY struct NCDModuleIndex_module +#define CHASH_PARAM_LINK NCDModuleIndex__mhash_link +#define CHASH_PARAM_KEY NCDModuleIndex__mhash_key +#define CHASH_PARAM_ARG int +#define CHASH_PARAM_NULL ((NCDModuleIndex__mhash_link)NULL) +#define CHASH_PARAM_DEREF(arg, link) (link) +#define CHASH_PARAM_ENTRYHASH(arg, entry) (badvpn_djb2_hash((const uint8_t *)(entry).ptr->imodule.module.type)) +#define CHASH_PARAM_KEYHASH(arg, key) (badvpn_djb2_hash((const uint8_t *)(key))) +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) (!strcmp((entry1).ptr->imodule.module.type, (entry2).ptr->imodule.module.type)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) (!strcmp((key1), (entry2).ptr->imodule.module.type)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/external/badvpn_dns/ncd/NCDObject.c b/external/badvpn_dns/ncd/NCDObject.c new file mode 100644 index 00000000..c2f4cadd --- /dev/null +++ b/external/badvpn_dns/ncd/NCDObject.c @@ -0,0 +1,40 @@ +/** + * @file NCDObject.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "NCDObject.h" + +int NCDObject_no_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + return 0; +} + +int NCDObject_no_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + return 0; +} diff --git a/external/badvpn_dns/ncd/NCDObject.h b/external/badvpn_dns/ncd/NCDObject.h new file mode 100644 index 00000000..237ec723 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDObject.h @@ -0,0 +1,356 @@ +/** + * @file NCDObject.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDOBJECT_H +#define BADVPN_NCDOBJECT_H + +#include + +#include +#include +#include +#include + +/** + * Represents an NCD object. + * Objects expose the following functionalities: + * - resolving variables by name, + * - resolving objects by name, + * - provide information for calling method-like statements. + * + * The NCDObject structure must not be stored persistently; it is only + * valid at the time it was obtained, and any change of state in the + * execution of the NCD program may render the object invalid. + * However, the structure does not contain any resources, and can freely + * be passed around by value. + */ +typedef struct NCDObject_s NCDObject; + +/** + * Callback function for variable resolution requests. + * + * @param obj const pointer to the NCDObject this is being called for. + * {@link NCDObject_DataPtr} and {@link NCDObject_DataInt} can be + * used to retrieve state information which was passed to + * {@link NCDObject_Build} or {@link NCDObject_BuildFull}. + * @param name name of the variable being resolved, in form of an {@link NCDStringIndex} + * string identifier + * @param mem pointer to the memory object where the resulting value should be + * constructed + * @param out_value If the variable exists, *out_value should be set to the value + * reference to the result value. If the variable exists but there + * was an error constructing the value, should be set to an + * invalid value reference. Can be modified even if the variable + * does not exist. + * @return 1 if the variable exists, 0 if not + */ +typedef int (*NCDObject_func_getvar) (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +/** + * Callback function for object resolution requests. + * + * @param obj const pointer to the NCDObject this is being called for. + * {@link NCDObject_DataPtr} and {@link NCDObject_DataInt} can be + * used to retrieve state information which was passed to + * {@link NCDObject_Build} or {@link NCDObject_BuildFull}. + * @param name name of the object being resolved, in form of an {@link NCDStringIndex} + * string identifier + * @param out_object If the object exists, *out_object should be set to the result + * object. Can be modified even if the object does not exist. + * @return 1 if the object exists, 0 if not + */ +typedef int (*NCDObject_func_getobj) (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); + +struct NCDObject_s { + NCD_string_id_t type; + int data_int; + void *data_ptr; + void *method_user; + NCDObject_func_getvar func_getvar; + NCDObject_func_getobj func_getobj; +}; + +/** + * Basic object construction function. + * This is equivalent to calling {@link NCDObject_BuildFull} with data_int=0 + * and method_user=data_ptr. See that function for detailed documentation. + */ +static NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj); + +/** + * Constructs an {@link NCDObject} structure. + * This is the full version where all supported parameters have to be provided. + * In most cases, {@link NCDObject_Build} will suffice. + * + * @param type type of the object for the purpose of calling method-like statements + * on the object, in form of an {@link NCDStringIndex} string identifier. + * May be set to -1 if the object has no methods. + * @param data_ptr state-keeping pointer which can be restored from callbacks using + * {@link NCDObject_DataPtr} + * @param data_int state-keeping integer which can be restored from callbacks using + * {@link NCDObject_DataInt} + * @param method_user state-keeping pointer to be passed to new method-like statements + * created using this object. The value of this argument will be + * available as params->method_user within the {@link NCDModule_func_new2} + * module backend callback. + * @param func_getvar callback for resolving variables within the object. This must not + * be NULL; if the object exposes no variables, pass {@link NCDObject_no_getvar}. + * @param func_getobj callback for resolving objects within the object. This must not + * be NULL; if the object exposes no objects, pass {@link NCDObject_no_getobj}. + * @return an NCDObject structure encapsulating the information given + */ +static NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj); + +/** + * Returns the 'type' attribute; see {@link NCDObject_BuildFull}. + */ +static NCD_string_id_t NCDObject_Type (const NCDObject *o); + +/** + * Returns the 'data_ptr' attribute; see {@link NCDObject_BuildFull}. + */ +static void * NCDObject_DataPtr (const NCDObject *o); + +/** + * Returns the 'data_int' attribute; see {@link NCDObject_BuildFull}. + */ +static int NCDObject_DataInt (const NCDObject *o); + +/** + * Returns the 'method_user' attribute; see {@link NCDObject_BuildFull}. + */ +static void * NCDObject_MethodUser (const NCDObject *o); + +/** + * Attempts to resolve a variable within the object. + * This just calls {@link NCDObject_func_getvar}, but also has some assertions to detect + * incorrect behavior of the callback. + */ +static int NCDObject_GetVar (const NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +/** + * Attempts to resolve an object within the object. + * This just calls {@link NCDObject_func_getobj}, but also has some assertions to detect + * incorrect behavior of the callback. + */ +static int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_object) WARN_UNUSED; + +/** + * Resolves a variable expression starting with this object. + * A variable expression is usually represented in dotted form, + * e.g. object1.object2.variable (for a named variable) or object1.object2.object3 + * (for an empty string variable). This function however receives the expression + * as an array of string identifiers. + * + * Consult the implementation for exact semantics of variable expression resolution. + * + * @param o object to start the resolution with + * @param names pointer to an array of names for the resolution. May be NULL if num_names is 0. + * @param num_names number in names in the array + * @param mem pointer to the memory object where the resulting value + * should be constructed + * @param out_value If the variable exists, *out_value will be set to the value + * reference to the result value. If the variable exists but there + * was an error constructing the value, will be set to an + * invalid value reference. May be modified even if the variable + * does not exist. + * @return 1 if the variable exists, 0 if not + */ +static int NCDObject_ResolveVarExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +/** + * Resolves an object expression starting with this object. + * An object expression is usually represented in dotted form, + * e.g. object1.object2.object3. This function however receives the expression + * as an array of string identifiers. + * + * Consult the implementation for exact semantics of object expression resolution. + * + * @param o object to start the resolution with + * @param names pointer to an array of names for the resolution. May be NULL if num_names is 0. + * @param num_names number in names in the array + * @param out_object If the object exists, *out_object will be set to the result + * object. May be modified even if the object does not exist. + * @return 1 if the object exists, 0 if not + */ +static int NCDObject_ResolveObjExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) WARN_UNUSED; + +/** + * Returns 0. This can be used as a dummy variable resolution callback. + */ +int NCDObject_no_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); + +/** + * Returns 0. This can be used as a dummy object resolution callback. + */ +int NCDObject_no_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); + +// + +NCDObject NCDObject_Build (NCD_string_id_t type, void *data_ptr, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj) +{ + ASSERT(type >= -1) + ASSERT(func_getvar) + ASSERT(func_getobj) + + NCDObject obj; + obj.type = type; + obj.data_int = 0; + obj.data_ptr = data_ptr; + obj.method_user = data_ptr; + obj.func_getvar = func_getvar; + obj.func_getobj = func_getobj; + + return obj; +} + +NCDObject NCDObject_BuildFull (NCD_string_id_t type, void *data_ptr, int data_int, void *method_user, NCDObject_func_getvar func_getvar, NCDObject_func_getobj func_getobj) +{ + ASSERT(type >= -1) + ASSERT(func_getvar) + ASSERT(func_getobj) + + NCDObject obj; + obj.type = type; + obj.data_int = data_int; + obj.data_ptr = data_ptr; + obj.method_user = method_user; + obj.func_getvar = func_getvar; + obj.func_getobj = func_getobj; + + return obj; +} + +NCD_string_id_t NCDObject_Type (const NCDObject *o) +{ + return o->type; +} + +void * NCDObject_DataPtr (const NCDObject *o) +{ + return o->data_ptr; +} + +int NCDObject_DataInt (const NCDObject *o) +{ + return o->data_int; +} + +void * NCDObject_MethodUser (const NCDObject *o) +{ + return o->method_user; +} + +int NCDObject_GetVar (const NCDObject *o, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(name >= 0) + ASSERT(mem) + ASSERT(out_value) + + int res = o->func_getvar(o, name, mem, out_value); + + ASSERT(res == 0 || res == 1) + ASSERT(res == 0 || (NCDVal_Assert(*out_value), 1)) + + return res; +} + +int NCDObject_GetObj (const NCDObject *o, NCD_string_id_t name, NCDObject *out_object) +{ + ASSERT(name >= 0) + ASSERT(out_object) + + int res = o->func_getobj(o, name, out_object); + + ASSERT(res == 0 || res == 1) + + return res; +} + +static NCDObject NCDObject__dig_into_object (NCDObject object) +{ + NCDObject obj2; + while (NCDObject_GetObj(&object, NCD_STRING_EMPTY, &obj2)) { + object = obj2; + } + + return object; +} + +int NCDObject_ResolveVarExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(num_names == 0 || names) + ASSERT(mem) + ASSERT(out_value) + + NCDObject object = NCDObject__dig_into_object(*o); + + while (num_names > 0) { + NCDObject obj2; + if (!NCDObject_GetObj(&object, *names, &obj2)) { + if (num_names == 1 && NCDObject_GetVar(&object, *names, mem, out_value)) { + return 1; + } + + return 0; + } + + object = NCDObject__dig_into_object(obj2); + + names++; + num_names--; + } + + return NCDObject_GetVar(&object, NCD_STRING_EMPTY, mem, out_value); +} + +int NCDObject_ResolveObjExprCompact (const NCDObject *o, const NCD_string_id_t *names, size_t num_names, NCDObject *out_object) +{ + ASSERT(num_names == 0 || names) + ASSERT(out_object) + + NCDObject object = NCDObject__dig_into_object(*o); + + while (num_names > 0) { + NCDObject obj2; + if (!NCDObject_GetObj(&object, *names, &obj2)) { + return 0; + } + + object = NCDObject__dig_into_object(obj2); + + names++; + num_names--; + } + + *out_object = object; + return 1; +} + +#endif diff --git a/external/badvpn_dns/ncd/NCDPlaceholderDb.c b/external/badvpn_dns/ncd/NCDPlaceholderDb.c new file mode 100644 index 00000000..906509d1 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDPlaceholderDb.c @@ -0,0 +1,127 @@ +/** + * @file NCDPlaceholderDb.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "NCDPlaceholderDb.h" + +#include + +int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index) +{ + ASSERT(string_index) + + o->count = 0; + o->capacity = 1; + o->string_index = string_index; + + if (!(o->arr = BAllocArray(o->capacity, sizeof(o->arr[0])))) { + BLog(BLOG_ERROR, "NCDPlaceholderDb_Init failed"); + return 0; + } + + return 1; +} + +void NCDPlaceholderDb_Free (NCDPlaceholderDb *o) +{ + for (size_t i = 0; i < o->count; i++) { + BFree(o->arr[i].varnames); + } + + BFree(o->arr); +} + +int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int *out_plid) +{ + ASSERT(varname) + ASSERT(out_plid) + ASSERT(o->count <= o->capacity) + ASSERT(o->capacity > 0) + + if (o->count == o->capacity) { + if (o->capacity > SIZE_MAX / 2) { + BLog(BLOG_ERROR, "too many placeholder entries (cannot resize)"); + return 0; + } + size_t newcap = 2 * o->capacity; + + struct NCDPlaceholderDb__entry *newarr = BAllocArray(newcap, sizeof(newarr[0])); + if (!newarr) { + BLog(BLOG_ERROR, "BAllocArray failed"); + return 0; + } + + memcpy(newarr, o->arr, o->count * sizeof(newarr[0])); + BFree(o->arr); + + o->arr = newarr; + o->capacity = newcap; + } + + ASSERT(o->count < o->capacity) + + if (o->count > INT_MAX) { + BLog(BLOG_ERROR, "too many placeholder entries (cannot fit integer)"); + return 0; + } + + NCD_string_id_t *varnames; + size_t num_names; + if (!ncd_make_name_indices(o->string_index, varname, &varnames, &num_names)) { + BLog(BLOG_ERROR, "ncd_make_name_indices failed"); + return 0; + } + + *out_plid = o->count; + + o->arr[o->count].varnames = varnames; + o->arr[o->count].num_names = num_names; + o->count++; + + return 1; +} + +void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names) +{ + ASSERT(plid >= 0) + ASSERT(plid < o->count) + ASSERT(out_varnames) + ASSERT(out_num_names) + + *out_varnames = o->arr[plid].varnames; + *out_num_names = o->arr[plid].num_names; +} diff --git a/external/badvpn_dns/ncd/NCDPlaceholderDb.h b/external/badvpn_dns/ncd/NCDPlaceholderDb.h new file mode 100644 index 00000000..d6a1f40c --- /dev/null +++ b/external/badvpn_dns/ncd/NCDPlaceholderDb.h @@ -0,0 +1,95 @@ +/** + * @file NCDPlaceholderDb.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDPLACEHOLDERDB_H +#define BADVPN_NCDPLACEHOLDERDB_H + +#include + +#include +#include + +struct NCDPlaceholderDb__entry { + NCD_string_id_t *varnames; + size_t num_names; +}; + +/** + * Associates variable placeholder numbers to variable names. + * This is populated by {@link NCDInterpProcess_Init} when converting the {@link NCDValue} + * objects in the AST to compact representations in {@link NCDValMem}. Variables are + * replaced with placeholder identifiers (integers), which this object associates + * with their names. + * During interpretation, when a statement is being initialized, the compact form held + * by {@link NCDInterpProcess} is byte-copied, and placeholders are replaced with the + * values of corresponding variables using {@link NCDVal_ReplacePlaceholders}. + */ +typedef struct { + struct NCDPlaceholderDb__entry *arr; + size_t count; + size_t capacity; + NCDStringIndex *string_index; +} NCDPlaceholderDb; + +/** + * Initializes the placeholder database. + * Returns 1 on success, and 0 on failure. + */ +int NCDPlaceholderDb_Init (NCDPlaceholderDb *o, NCDStringIndex *string_index) WARN_UNUSED; + +/** + * Frees the placeholder database. + */ +void NCDPlaceholderDb_Free (NCDPlaceholderDb *o); + +/** + * Adds a variable to the database. + * + * @param varname name of the variable (text, including dots). Must not be NULL. + * @param out_plid on success, the placeholder identifier will be returned here. Must + * not be NULL. + * @return 1 on success, 0 on failure + */ +int NCDPlaceholderDb_AddVariable (NCDPlaceholderDb *o, const char *varname, int *out_plid) WARN_UNUSED; + +/** + * Retrieves the name of the variable associated with a placeholder identifier. + * + * @param plid placeholder identifier; must have been previously provided by + * {@link NCDPlaceholderDb_AddVariable}. + * @return name of the variable, split by dots. The returned value points to + * an array of pointers to strings, which is terminated by a NULL + * pointer. These all point to internal data in the placeholder + * database; they must not be modified, and remain valid until the + * database is freed. + * Note that there will always be at least one string in the result. + */ +void NCDPlaceholderDb_GetVariable (NCDPlaceholderDb *o, int plid, const NCD_string_id_t **out_varnames, size_t *out_num_names); + +#endif diff --git a/external/badvpn_dns/ncd/NCDStringIndex.c b/external/badvpn_dns/ncd/NCDStringIndex.c new file mode 100644 index 00000000..87a231db --- /dev/null +++ b/external/badvpn_dns/ncd/NCDStringIndex.c @@ -0,0 +1,262 @@ +/** + * @file NCDStringIndex.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "NCDStringIndex.h" + +#include "NCDStringIndex_hash.h" +#include + +#define GROWARRAY_NAME Array +#define GROWARRAY_OBJECT_TYPE NCDStringIndex +#define GROWARRAY_ARRAY_MEMBER entries +#define GROWARRAY_CAPACITY_MEMBER entries_capacity +#define GROWARRAY_MAX_CAPACITY NCD_STRING_ID_MAX +#include + +#include + +// NOTE: keep synchronized with static_strings.h +static const char *static_strings[] = { + "", + "_args", + "_arg0", + "_arg1", + "_arg2", + "_arg3", + "_arg4", + "_arg5", + "_arg6", + "_arg7", + "_arg8", + "_arg9", + "_arg10", + "_arg11", + "_arg12", + "_arg13", + "_arg14", + "_arg15", + "_arg16", + "_arg17", + "_arg18", + "_arg19", + "true", + "false", + "", + "_caller", + "succeeded", + "is_error", + "not_eof", + "length", + "type", + "exit_status", + "size" +}; + +static NCD_string_id_t do_get (NCDStringIndex *o, const char *str, size_t str_len) +{ + ASSERT(str) + + NCDStringIndex_hash_key key = {str, str_len}; + NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, key); + ASSERT(ref.link == -1 || ref.link >= 0) + ASSERT(ref.link == -1 || ref.link < o->entries_size) + ASSERT(ref.link == -1 || (ref.ptr->str_len == str_len && !memcmp(ref.ptr->str, str, str_len))) + + if (ref.link != -1) { + return ref.link; + } + + if (o->entries_size == o->entries_capacity) { + if (!Array_DoubleUp(o)) { + BLog(BLOG_ERROR, "Array_DoubleUp failed"); + return -1; + } + + if (!NCDStringIndex__Hash_MultiplyBuckets(&o->hash, o->entries, 1)) { + BLog(BLOG_ERROR, "NCDStringIndex__Hash_MultiplyBuckets failed"); + return -1; + } + } + + ASSERT(o->entries_size < o->entries_capacity) + + struct NCDStringIndex__entry *entry = &o->entries[o->entries_size]; + + if (!(entry->str = b_strdup_bin(str, str_len))) { + BLog(BLOG_ERROR, "b_strdup_bin failed"); + return -1; + } + entry->str_len = str_len; + entry->has_nulls = !!memchr(str, '\0', str_len); + + NCDStringIndex__HashRef newref = {entry, o->entries_size}; + int res = NCDStringIndex__Hash_Insert(&o->hash, o->entries, newref, NULL); + ASSERT_EXECUTE(res) + + return o->entries_size++; +} + +int NCDStringIndex_Init (NCDStringIndex *o) +{ + o->entries_size = 0; + + if (!Array_Init(o, NCDSTRINGINDEX_INITIAL_CAPACITY)) { + BLog(BLOG_ERROR, "Array_Init failed"); + goto fail0; + } + + if (!NCDStringIndex__Hash_Init(&o->hash, NCDSTRINGINDEX_INITIAL_HASH_BUCKETS)) { + BLog(BLOG_ERROR, "NCDStringIndex__Hash_Init failed"); + goto fail1; + } + + for (size_t i = 0; i < B_ARRAY_LENGTH(static_strings); i++) { + if (do_get(o, static_strings[i], strlen(static_strings[i])) < 0) { + goto fail2; + } + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + for (NCD_string_id_t i = 0; i < o->entries_size; i++) { + free(o->entries[i].str); + } + NCDStringIndex__Hash_Free(&o->hash); +fail1: + Array_Free(o); +fail0: + return 0; +} + +void NCDStringIndex_Free (NCDStringIndex *o) +{ + DebugObject_Free(&o->d_obj); + + for (NCD_string_id_t i = 0; i < o->entries_size; i++) { + free(o->entries[i].str); + } + + NCDStringIndex__Hash_Free(&o->hash); + Array_Free(o); +} + +NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return NCDStringIndex_LookupBin(o, str, strlen(str)); +} + +NCD_string_id_t NCDStringIndex_LookupBin (NCDStringIndex *o, const char *str, size_t str_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + NCDStringIndex_hash_key key = {str, str_len}; + NCDStringIndex__HashRef ref = NCDStringIndex__Hash_Lookup(&o->hash, o->entries, key); + ASSERT(ref.link == -1 || ref.link >= 0) + ASSERT(ref.link == -1 || ref.link < o->entries_size) + ASSERT(ref.link == -1 || (ref.ptr->str_len == str_len && !memcmp(ref.ptr->str, str, str_len))) + + return ref.link; +} + +NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return NCDStringIndex_GetBin(o, str, strlen(str)); +} + +NCD_string_id_t NCDStringIndex_GetBin (NCDStringIndex *o, const char *str, size_t str_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(str) + + return do_get(o, str, str_len); +} + +const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].str; +} + +size_t NCDStringIndex_Length (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].str_len; +} + +int NCDStringIndex_HasNulls (NCDStringIndex *o, NCD_string_id_t id) +{ + DebugObject_Access(&o->d_obj); + ASSERT(id >= 0) + ASSERT(id < o->entries_size) + ASSERT(o->entries[id].str) + + return o->entries[id].has_nulls; +} + +int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests) +{ + DebugObject_Access(&o->d_obj); + ASSERT(requests) + + while (requests->str) { + NCD_string_id_t id = NCDStringIndex_Get(o, requests->str); + if (id < 0) { + return 0; + } + requests->id = id; + requests++; + } + + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDStringIndex.h b/external/badvpn_dns/ncd/NCDStringIndex.h new file mode 100644 index 00000000..78e20ecf --- /dev/null +++ b/external/badvpn_dns/ncd/NCDStringIndex.h @@ -0,0 +1,83 @@ +/** + * @file NCDStringIndex.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_STRING_INDEX_H +#define BADVPN_NCD_STRING_INDEX_H + +#include + +#include +#include +#include + +#define NCDSTRINGINDEX_INITIAL_CAPACITY 256 +#define NCDSTRINGINDEX_INITIAL_HASH_BUCKETS 256 + +typedef int NCD_string_id_t; + +#define NCD_STRING_ID_MAX INT_MAX + +struct NCDStringIndex__entry { + char *str; + size_t str_len; + int has_nulls; + NCD_string_id_t hash_next; +}; + +typedef struct { const char *str; size_t len; } NCDStringIndex_hash_key; +typedef struct NCDStringIndex__entry *NCDStringIndex_hash_arg; + +#include "NCDStringIndex_hash.h" +#include + +typedef struct { + struct NCDStringIndex__entry *entries; + NCD_string_id_t entries_capacity; + NCD_string_id_t entries_size; + NCDStringIndex__Hash hash; + DebugObject d_obj; +} NCDStringIndex; + +struct NCD_string_request { + const char *str; + NCD_string_id_t id; +}; + +int NCDStringIndex_Init (NCDStringIndex *o) WARN_UNUSED; +void NCDStringIndex_Free (NCDStringIndex *o); +NCD_string_id_t NCDStringIndex_Lookup (NCDStringIndex *o, const char *str); +NCD_string_id_t NCDStringIndex_LookupBin (NCDStringIndex *o, const char *str, size_t str_len); +NCD_string_id_t NCDStringIndex_Get (NCDStringIndex *o, const char *str); +NCD_string_id_t NCDStringIndex_GetBin (NCDStringIndex *o, const char *str, size_t str_len); +const char * NCDStringIndex_Value (NCDStringIndex *o, NCD_string_id_t id); +size_t NCDStringIndex_Length (NCDStringIndex *o, NCD_string_id_t id); +int NCDStringIndex_HasNulls (NCDStringIndex *o, NCD_string_id_t id); +int NCDStringIndex_GetRequests (NCDStringIndex *o, struct NCD_string_request *requests) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDStringIndex_hash.h b/external/badvpn_dns/ncd/NCDStringIndex_hash.h new file mode 100644 index 00000000..9a751fbc --- /dev/null +++ b/external/badvpn_dns/ncd/NCDStringIndex_hash.h @@ -0,0 +1,13 @@ +#define CHASH_PARAM_NAME NCDStringIndex__Hash +#define CHASH_PARAM_ENTRY struct NCDStringIndex__entry +#define CHASH_PARAM_LINK NCD_string_id_t +#define CHASH_PARAM_KEY NCDStringIndex_hash_key +#define CHASH_PARAM_ARG NCDStringIndex_hash_arg +#define CHASH_PARAM_NULL ((NCD_string_id_t)-1) +#define CHASH_PARAM_DEREF(arg, link) (&(arg)[(link)]) +#define CHASH_PARAM_ENTRYHASH(arg, entry) badvpn_djb2_hash_bin((const uint8_t *)(entry).ptr->str, (entry).ptr->str_len) +#define CHASH_PARAM_KEYHASH(arg, key) badvpn_djb2_hash_bin((const uint8_t *)(key).str, (key).len) +#define CHASH_PARAM_ENTRYHASH_IS_CHEAP 0 +#define CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) ((entry1).ptr->str_len == (entry2).ptr->str_len && !memcmp((entry1).ptr->str, (entry2).ptr->str, (entry1).ptr->str_len)) +#define CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) ((key1).len == (entry2).ptr->str_len && !memcmp((key1).str, (entry2).ptr->str, (key1).len)) +#define CHASH_PARAM_ENTRY_NEXT hash_next diff --git a/external/badvpn_dns/ncd/NCDSugar.c b/external/badvpn_dns/ncd/NCDSugar.c new file mode 100644 index 00000000..669bec83 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDSugar.c @@ -0,0 +1,253 @@ +/** + * @file NCDSugar.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "NCDSugar.h" + +struct desugar_state { + NCDProgram *prog; + size_t template_name_ctr; +}; + +static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val); +static int desugar_block (struct desugar_state *state, NCDBlock *block); +static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next); +static int desugar_foreach (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next); + +static int add_template (struct desugar_state *state, NCDBlock block, NCDValue *out_name_val) +{ + char name[40]; + snprintf(name, sizeof(name), "__tmpl%zu", state->template_name_ctr); + state->template_name_ctr++; + + if (!desugar_block(state, &block)) { + NCDBlock_Free(&block); + return 0; + } + + NCDProcess proc_tmp; + if (!NCDProcess_Init(&proc_tmp, 1, name, block)) { + NCDBlock_Free(&block); + return 0; + } + + NCDProgramElem elem; + NCDProgramElem_InitProcess(&elem, proc_tmp); + + if (!NCDProgram_PrependElem(state->prog, elem)) { + NCDProgramElem_Free(&elem); + return 0; + } + + if (!NCDValue_InitString(out_name_val, name)) { + return 0; + } + + return 1; +} + +static int desugar_block (struct desugar_state *state, NCDBlock *block) +{ + NCDStatement *stmt = NCDBlock_FirstStatement(block); + + while (stmt) { + switch (NCDStatement_Type(stmt)) { + case NCDSTATEMENT_REG: { + stmt = NCDBlock_NextStatement(block, stmt); + } break; + + case NCDSTATEMENT_IF: { + if (!desugar_if(state, block, stmt, &stmt)) { + return 0; + } + } break; + + case NCDSTATEMENT_FOREACH: { + if (!desugar_foreach(state, block, stmt, &stmt)) { + return 0; + } + } break; + + default: ASSERT(0); + } + } + + return 1; +} + +static int desugar_if (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next) +{ + ASSERT(NCDStatement_Type(stmt) == NCDSTATEMENT_IF) + + NCDValue args; + NCDValue_InitList(&args); + + NCDIfBlock *ifblock = NCDStatement_IfBlock(stmt); + + while (NCDIfBlock_FirstIf(ifblock)) { + NCDIf ifc = NCDIfBlock_GrabIf(ifblock, NCDIfBlock_FirstIf(ifblock)); + + NCDValue if_cond; + NCDBlock if_block; + NCDIf_FreeGrab(&ifc, &if_cond, &if_block); + + if (!NCDValue_ListAppend(&args, if_cond)) { + NCDValue_Free(&if_cond); + NCDBlock_Free(&if_block); + goto fail; + } + + NCDValue action_arg; + if (!add_template(state, if_block, &action_arg)) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, action_arg)) { + NCDValue_Free(&action_arg); + goto fail; + } + } + + if (NCDStatement_IfElse(stmt)) { + NCDBlock else_block = NCDStatement_IfGrabElse(stmt); + + NCDValue action_arg; + if (!add_template(state, else_block, &action_arg)) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, action_arg)) { + NCDValue_Free(&action_arg); + goto fail; + } + } + + NCDStatement new_stmt; + if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "embcall2_multif", args)) { + goto fail; + } + + stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt); + + *out_next = NCDBlock_NextStatement(block, stmt); + return 1; + +fail: + NCDValue_Free(&args); + return 0; +} + +static int desugar_foreach (struct desugar_state *state, NCDBlock *block, NCDStatement *stmt, NCDStatement **out_next) +{ + ASSERT(NCDStatement_Type(stmt) == NCDSTATEMENT_FOREACH) + + NCDValue args; + NCDValue_InitList(&args); + + NCDValue collection; + NCDBlock foreach_block; + NCDStatement_ForeachGrab(stmt, &collection, &foreach_block); + + NCDValue template_arg; + if (!add_template(state, foreach_block, &template_arg)) { + NCDValue_Free(&collection); + goto fail; + } + + if (!NCDValue_ListAppend(&args, collection)) { + NCDValue_Free(&template_arg); + NCDValue_Free(&collection); + goto fail; + } + + if (!NCDValue_ListAppend(&args, template_arg)) { + NCDValue_Free(&template_arg); + goto fail; + } + + NCDValue name1_arg; + if (!NCDValue_InitString(&name1_arg, NCDStatement_ForeachName1(stmt))) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, name1_arg)) { + NCDValue_Free(&name1_arg); + goto fail; + } + + if (NCDStatement_ForeachName2(stmt)) { + NCDValue name2_arg; + if (!NCDValue_InitString(&name2_arg, NCDStatement_ForeachName2(stmt))) { + goto fail; + } + + if (!NCDValue_ListAppend(&args, name2_arg)) { + NCDValue_Free(&name2_arg); + goto fail; + } + } + + NCDStatement new_stmt; + if (!NCDStatement_InitReg(&new_stmt, NCDStatement_Name(stmt), NULL, "foreach_emb", args)) { + goto fail; + } + + stmt = NCDBlock_ReplaceStatement(block, stmt, new_stmt); + + *out_next = NCDBlock_NextStatement(block, stmt); + return 1; + +fail: + NCDValue_Free(&args); + return 0; +} + +int NCDSugar_Desugar (NCDProgram *prog) +{ + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE)) + ASSERT(!NCDProgram_ContainsElemType(prog, NCDPROGRAMELEM_INCLUDE_GUARD)) + + struct desugar_state state; + state.prog = prog; + state.template_name_ctr = 0; + + for (NCDProgramElem *elem = NCDProgram_FirstElem(prog); elem; elem = NCDProgram_NextElem(prog, elem)) { + ASSERT(NCDProgramElem_Type(elem) == NCDPROGRAMELEM_PROCESS) + NCDProcess *proc = NCDProgramElem_Process(elem); + + if (!desugar_block(&state, NCDProcess_Block(proc))) { + return 0; + } + } + + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDSugar.h b/external/badvpn_dns/ncd/NCDSugar.h new file mode 100644 index 00000000..0aa0ad23 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDSugar.h @@ -0,0 +1,38 @@ +/** + * @file NCDSugar.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDSUGAR_H +#define BADVPN_NCDSUGAR_H + +#include +#include + +int NCDSugar_Desugar (NCDProgram *prog) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDVal.c b/external/badvpn_dns/ncd/NCDVal.c new file mode 100644 index 00000000..aecb82b8 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDVal.c @@ -0,0 +1,2065 @@ +/** + * @file NCDVal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "NCDVal.h" + +#include + +#define TYPE_MASK_EXTERNAL_TYPE ((1 << 3) - 1) +#define TYPE_MASK_INTERNAL_TYPE ((1 << 5) - 1) +#define TYPE_SHIFT_DEPTH 5 + +#define STOREDSTRING_TYPE (NCDVAL_STRING | (0 << 3)) +#define IDSTRING_TYPE (NCDVAL_STRING | (1 << 3)) +#define EXTERNALSTRING_TYPE (NCDVAL_STRING | (2 << 3)) +#define COMPOSEDSTRING_TYPE (NCDVAL_STRING | (3 << 3)) + +static int make_type (int internal_type, int depth) +{ + ASSERT(internal_type == NCDVAL_LIST || + internal_type == NCDVAL_MAP || + internal_type == STOREDSTRING_TYPE || + internal_type == IDSTRING_TYPE || + internal_type == EXTERNALSTRING_TYPE || + internal_type == COMPOSEDSTRING_TYPE) + ASSERT(depth >= 0) + ASSERT(depth <= NCDVAL_MAX_DEPTH) + + return (internal_type | (depth << TYPE_SHIFT_DEPTH)); +} + +static int get_external_type (int type) +{ + return (type & TYPE_MASK_EXTERNAL_TYPE); +} + +static int get_internal_type (int type) +{ + return (type & TYPE_MASK_INTERNAL_TYPE); +} + +static int get_depth (int type) +{ + return (type >> TYPE_SHIFT_DEPTH); +} + +static int bump_depth (int *type_ptr, int elem_depth) +{ + if (get_depth(*type_ptr) < elem_depth + 1) { + if (elem_depth + 1 > NCDVAL_MAX_DEPTH) { + return 0; + } + *type_ptr = make_type(get_internal_type(*type_ptr), elem_depth + 1); + } + + return 1; +} + +static void * NCDValMem__BufAt (NCDValMem *o, NCDVal__idx idx) +{ + ASSERT(idx >= 0) + ASSERT(idx < o->used) + + return (o->buf ? o->buf : o->fastbuf) + idx; +} + +static NCDVal__idx NCDValMem__Alloc (NCDValMem *o, NCDVal__idx alloc_size, NCDVal__idx align) +{ + NCDVal__idx mod = o->used % align; + NCDVal__idx align_extra = mod ? (align - mod) : 0; + + if (alloc_size > NCDVAL_MAXIDX - align_extra) { + return -1; + } + NCDVal__idx aligned_alloc_size = align_extra + alloc_size; + + if (aligned_alloc_size > o->size - o->used) { + NCDVal__idx newsize = (o->buf ? o->size : NCDVAL_FIRST_SIZE); + while (aligned_alloc_size > newsize - o->used) { + if (newsize > NCDVAL_MAXIDX / 2) { + return -1; + } + newsize *= 2; + } + + char *newbuf; + + if (!o->buf) { + newbuf = malloc(newsize); + if (!newbuf) { + return -1; + } + memcpy(newbuf, o->fastbuf, o->used); + } else { + newbuf = realloc(o->buf, newsize); + if (!newbuf) { + return -1; + } + } + + o->buf = newbuf; + o->size = newsize; + } + + NCDVal__idx idx = o->used + align_extra; + o->used += aligned_alloc_size; + + return idx; +} + +static NCDValRef NCDVal__Ref (NCDValMem *mem, NCDVal__idx idx) +{ + ASSERT(idx == -1 || mem) + + NCDValRef ref = {mem, idx}; + return ref; +} + +static void NCDVal__AssertMem (NCDValMem *mem) +{ + ASSERT(mem) + ASSERT(mem->size >= 0) + ASSERT(mem->used >= 0) + ASSERT(mem->used <= mem->size) + ASSERT(mem->buf || mem->size == NCDVAL_FASTBUF_SIZE) + ASSERT(!mem->buf || mem->size >= NCDVAL_FIRST_SIZE) +} + +static void NCDVal_AssertExternal (NCDValMem *mem, const void *e_buf, size_t e_len) +{ +#ifndef NDEBUG + const char *e_cbuf = e_buf; + char *buf = (mem->buf ? mem->buf : mem->fastbuf); + ASSERT(e_cbuf >= buf + mem->size || e_cbuf + e_len <= buf) +#endif +} + +static void NCDVal__AssertValOnly (NCDValMem *mem, NCDVal__idx idx) +{ + // placeholders + if (idx < -1) { + return; + } + + ASSERT(idx >= 0) + ASSERT(idx + sizeof(int) <= mem->used) + +#ifndef NDEBUG + int *type_ptr = NCDValMem__BufAt(mem, idx); + + ASSERT(get_depth(*type_ptr) >= 0) + ASSERT(get_depth(*type_ptr) <= NCDVAL_MAX_DEPTH) + + switch (get_internal_type(*type_ptr)) { + case STOREDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__string) <= mem->used) + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + ASSERT(str_e->length >= 0) + ASSERT(idx + sizeof(struct NCDVal__string) + str_e->length + 1 <= mem->used) + } break; + case NCDVAL_LIST: { + ASSERT(idx + sizeof(struct NCDVal__list) <= mem->used) + struct NCDVal__list *list_e = NCDValMem__BufAt(mem, idx); + ASSERT(list_e->maxcount >= 0) + ASSERT(list_e->count >= 0) + ASSERT(list_e->count <= list_e->maxcount) + ASSERT(idx + sizeof(struct NCDVal__list) + list_e->maxcount * sizeof(NCDVal__idx) <= mem->used) + } break; + case NCDVAL_MAP: { + ASSERT(idx + sizeof(struct NCDVal__map) <= mem->used) + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, idx); + ASSERT(map_e->maxcount >= 0) + ASSERT(map_e->count >= 0) + ASSERT(map_e->count <= map_e->maxcount) + ASSERT(idx + sizeof(struct NCDVal__map) + map_e->maxcount * sizeof(struct NCDVal__mapelem) <= mem->used) + } break; + case IDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__idstring) <= mem->used) + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(mem, idx); + ASSERT(ids_e->string_id >= 0) + ASSERT(ids_e->string_index) + } break; + case EXTERNALSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__externalstring) <= mem->used) + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx); + ASSERT(exs_e->data) + ASSERT(!exs_e->ref.target || exs_e->ref.next >= -1) + ASSERT(!exs_e->ref.target || exs_e->ref.next < mem->used) + } break; + case COMPOSEDSTRING_TYPE: { + ASSERT(idx + sizeof(struct NCDVal__composedstring) <= mem->used) + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(mem, idx); + ASSERT(cms_e->func_getptr) + ASSERT(!cms_e->ref.target || cms_e->ref.next >= -1) + ASSERT(!cms_e->ref.target || cms_e->ref.next < mem->used) + } break; + default: ASSERT(0); + } +#endif +} + +static void NCDVal__AssertVal (NCDValRef val) +{ + NCDVal__AssertMem(val.mem); + NCDVal__AssertValOnly(val.mem, val.idx); +} + +static NCDValMapElem NCDVal__MapElem (NCDVal__idx elemidx) +{ + ASSERT(elemidx >= 0 || elemidx == -1) + + NCDValMapElem me = {elemidx}; + return me; +} + +static void NCDVal__MapAssertElemOnly (NCDValRef map, NCDVal__idx elemidx) +{ +#ifndef NDEBUG + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + ASSERT(elemidx >= map.idx + offsetof(struct NCDVal__map, elems)) + ASSERT(elemidx < map.idx + offsetof(struct NCDVal__map, elems) + map_e->count * sizeof(struct NCDVal__mapelem)) + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, elemidx); + NCDVal__AssertValOnly(map.mem, me_e->key_idx); + NCDVal__AssertValOnly(map.mem, me_e->val_idx); +#endif +} + +static void NCDVal__MapAssertElem (NCDValRef map, NCDValMapElem me) +{ + ASSERT(NCDVal_IsMap(map)) + NCDVal__MapAssertElemOnly(map, me.elemidx); +} + +static NCDVal__idx NCDVal__MapElemIdx (NCDVal__idx mapidx, NCDVal__idx pos) +{ + return mapidx + offsetof(struct NCDVal__map, elems) + pos * sizeof(struct NCDVal__mapelem); +} + +static int NCDVal__Depth (NCDValRef val) +{ + ASSERT(val.idx != -1) + + // handle placeholders + if (val.idx < 0) { + return 0; + } + + int *elem_type_ptr = NCDValMem__BufAt(val.mem, val.idx); + int depth = get_depth(*elem_type_ptr); + ASSERT(depth >= 0) + ASSERT(depth <= NCDVAL_MAX_DEPTH) + + return depth; +} + +static int NCDValMem__NeedRegisterLink (NCDValMem *mem, NCDVal__idx val_idx) +{ + NCDVal__AssertValOnly(mem, val_idx); + + return !(val_idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(mem, val_idx)) == COMPOSEDSTRING_TYPE; +} + +static int NCDValMem__RegisterLink (NCDValMem *mem, NCDVal__idx val_idx, NCDVal__idx link_idx) +{ + NCDVal__AssertValOnly(mem, val_idx); + ASSERT(NCDValMem__NeedRegisterLink(mem, val_idx)) + + NCDVal__idx cms_link_idx = NCDValMem__Alloc(mem, sizeof(struct NCDVal__cms_link), __alignof(struct NCDVal__cms_link)); + if (cms_link_idx < 0) { + return 0; + } + + struct NCDVal__cms_link *cms_link = NCDValMem__BufAt(mem, cms_link_idx); + cms_link->link_idx = link_idx; + cms_link->next_cms_link = mem->first_cms_link; + mem->first_cms_link = cms_link_idx; + + return 1; +} + +static void NCDValMem__PopLastRegisteredLink (NCDValMem *mem) +{ + ASSERT(mem->first_cms_link != -1) + + struct NCDVal__cms_link *cms_link = NCDValMem__BufAt(mem, mem->first_cms_link); + mem->first_cms_link = cms_link->next_cms_link; +} + +static NCDValRef NCDVal__CopyComposedStringToStored (NCDValRef val) +{ + ASSERT(NCDVal_IsComposedString(val)) + + struct NCDVal__composedstring cms_e = *(struct NCDVal__composedstring *)NCDValMem__BufAt(val.mem, val.idx); + + NCDValRef copy = NCDVal_NewStringUninitialized(val.mem, cms_e.length); + if (NCDVal_IsInvalid(copy)) { + return NCDVal_NewInvalid(); + } + + char *copy_data = (char *)NCDVal_StringData(copy); + + size_t pos = 0; + while (pos < cms_e.length) { + const char *chunk_data; + size_t chunk_len; + cms_e.func_getptr(cms_e.user, cms_e.offset + pos, &chunk_data, &chunk_len); + ASSERT(chunk_data) + ASSERT(chunk_len > 0) + if (chunk_len > cms_e.length - pos) { + chunk_len = cms_e.length - pos; + } + memcpy(copy_data + pos, chunk_data, chunk_len); + pos += chunk_len; + } + + return copy; +} + +static const char * NCDVal__composedstring_cstring_func (const b_cstring *cstr, size_t offset, size_t *out_length) +{ + ASSERT(offset < cstr->length) + ASSERT(out_length) + ASSERT(cstr->func == NCDVal__composedstring_cstring_func) + + size_t str_offset = cstr->user1.size; + NCDVal_ComposedString_func_getptr func_getptr = (NCDVal_ComposedString_func_getptr)cstr->user2.fptr; + void *user = cstr->user3.ptr; + + const char *data; + func_getptr(user, str_offset + offset, &data, out_length); + + ASSERT(data) + ASSERT(*out_length > 0) + + return data; +} + +#include "NCDVal_maptree.h" +#include + +void NCDValMem_Init (NCDValMem *o) +{ + o->buf = NULL; + o->size = NCDVAL_FASTBUF_SIZE; + o->used = 0; + o->first_ref = -1; + o->first_cms_link = -1; +} + +void NCDValMem_Free (NCDValMem *o) +{ + NCDVal__AssertMem(o); + + NCDVal__idx refidx = o->first_ref; + while (refidx != -1) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx); + ASSERT(ref->target) + BRefTarget_Deref(ref->target); + refidx = ref->next; + } + + if (o->buf) { + BFree(o->buf); + } +} + +int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other) +{ + NCDVal__AssertMem(other); + + o->size = other->size; + o->used = other->used; + o->first_ref = other->first_ref; + o->first_cms_link = other->first_cms_link; + + if (!other->buf) { + o->buf = NULL; + memcpy(o->fastbuf, other->fastbuf, other->used); + } else { + o->buf = BAlloc(other->size); + if (!o->buf) { + goto fail0; + } + memcpy(o->buf, other->buf, other->used); + } + + NCDVal__idx refidx = o->first_ref; + while (refidx != -1) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, refidx); + ASSERT(ref->target) + if (!BRefTarget_Ref(ref->target)) { + goto fail1; + } + refidx = ref->next; + } + + return 1; + +fail1:; + NCDVal__idx undo_refidx = o->first_ref; + while (undo_refidx != refidx) { + struct NCDVal__ref *ref = NCDValMem__BufAt(o, undo_refidx); + BRefTarget_Deref(ref->target); + undo_refidx = ref->next; + } + if (o->buf) { + BFree(o->buf); + } +fail0: + return 0; +} + +int NCDValMem_ConvertNonContinuousStrings (NCDValMem *o, NCDValRef *root_val) +{ + NCDVal__AssertMem(o); + ASSERT(root_val) + ASSERT(root_val->mem == o) + NCDVal__AssertValOnly(o, root_val->idx); + + while (o->first_cms_link != -1) { + struct NCDVal__cms_link cms_link = *(struct NCDVal__cms_link *)NCDValMem__BufAt(o, o->first_cms_link); + + NCDVal__idx val_idx = *(NCDVal__idx *)NCDValMem__BufAt(o, cms_link.link_idx); + NCDValRef val = NCDVal__Ref(o, val_idx); + ASSERT(NCDVal_IsComposedString(val)) + + NCDValRef copy = NCDVal__CopyComposedStringToStored(val); + if (NCDVal_IsInvalid(copy)) { + return 0; + } + + *(int *)NCDValMem__BufAt(o, cms_link.link_idx) = copy.idx; + + o->first_cms_link = cms_link.next_cms_link; + } + + if (NCDVal_IsComposedString(*root_val)) { + NCDValRef copy = NCDVal__CopyComposedStringToStored(*root_val); + if (NCDVal_IsInvalid(copy)) { + return 0; + } + *root_val = copy; + } + + return 1; +} + +void NCDVal_Assert (NCDValRef val) +{ + ASSERT(val.idx == -1 || (NCDVal__AssertVal(val), 1)) +} + +int NCDVal_IsInvalid (NCDValRef val) +{ + NCDVal_Assert(val); + + return (val.idx == -1); +} + +int NCDVal_IsPlaceholder (NCDValRef val) +{ + NCDVal_Assert(val); + + return (val.idx < -1); +} + +int NCDVal_Type (NCDValRef val) +{ + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return NCDVAL_PLACEHOLDER; + } + + int *type_ptr = NCDValMem__BufAt(val.mem, val.idx); + + return get_external_type(*type_ptr); +} + +NCDValRef NCDVal_NewInvalid (void) +{ + NCDValRef ref = {NULL, -1}; + return ref; +} + +NCDValRef NCDVal_NewPlaceholder (NCDValMem *mem, int plid) +{ + NCDVal__AssertMem(mem); + ASSERT(plid >= 0) + ASSERT(NCDVAL_MINIDX + plid < -1) + + NCDValRef ref = {mem, NCDVAL_MINIDX + plid}; + return ref; +} + +int NCDVal_PlaceholderId (NCDValRef val) +{ + ASSERT(NCDVal_IsPlaceholder(val)) + + return (val.idx - NCDVAL_MINIDX); +} + +NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val) +{ + NCDVal__AssertMem(mem); + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return NCDVal_NewPlaceholder(mem, NCDVal_PlaceholderId(val)); + } + + void *ptr = NCDValMem__BufAt(val.mem, val.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + + NCDVal__idx size = sizeof(struct NCDVal__string) + str_e->length + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + str_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__string *new_str_e = NCDValMem__BufAt(mem, idx); + + memcpy(new_str_e, str_e, size); + + return NCDVal__Ref(mem, idx); + } break; + + case NCDVAL_LIST: { + struct NCDVal__list *list_e = ptr; + + NCDVal__idx size = sizeof(struct NCDVal__list) + list_e->maxcount * sizeof(NCDVal__idx); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__list)); + if (idx < 0) { + goto fail; + } + + list_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__list *new_list_e = NCDValMem__BufAt(mem, idx); + + *new_list_e = *list_e; + + NCDVal__idx count = list_e->count; + + for (NCDVal__idx i = 0; i < count; i++) { + NCDValRef elem_copy = NCDVal_NewCopy(mem, NCDVal__Ref(val.mem, list_e->elem_indices[i])); + if (NCDVal_IsInvalid(elem_copy)) { + goto fail; + } + + if (NCDValMem__NeedRegisterLink(mem, elem_copy.idx)) { + if (!NCDValMem__RegisterLink(mem, elem_copy.idx, idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx))) { + goto fail; + } + } + + list_e = NCDValMem__BufAt(val.mem, val.idx); + new_list_e = NCDValMem__BufAt(mem, idx); + + new_list_e->elem_indices[i] = elem_copy.idx; + } + + return NCDVal__Ref(mem, idx); + } break; + + case NCDVAL_MAP: { + size_t count = NCDVal_MapCount(val); + + NCDValRef copy = NCDVal_NewMap(mem, count); + if (NCDVal_IsInvalid(copy)) { + goto fail; + } + + for (NCDValMapElem e = NCDVal_MapFirst(val); !NCDVal_MapElemInvalid(e); e = NCDVal_MapNext(val, e)) { + NCDValRef key_copy = NCDVal_NewCopy(mem, NCDVal_MapElemKey(val, e)); + NCDValRef val_copy = NCDVal_NewCopy(mem, NCDVal_MapElemVal(val, e)); + if (NCDVal_IsInvalid(key_copy) || NCDVal_IsInvalid(val_copy)) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(copy, key_copy, val_copy, &inserted)) { + goto fail; + } + ASSERT_EXECUTE(inserted) + } + + return copy; + } break; + + case IDSTRING_TYPE: { + NCDVal__idx size = sizeof(struct NCDVal__idstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__idstring)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(val.mem, val.idx); + struct NCDVal__idstring *new_ids_e = NCDValMem__BufAt(mem, idx); + + *new_ids_e = *ids_e; + + return NCDVal__Ref(mem, idx); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + + return NCDVal_NewExternalString(mem, exs_e->data, exs_e->length, exs_e->ref.target); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + + NCDValComposedStringResource resource; + resource.func_getptr = cms_e->func_getptr; + resource.user = cms_e->user; + resource.ref_target = cms_e->ref.target; + + return NCDVal_NewComposedString(mem, resource, cms_e->offset, cms_e->length); + } break; + + default: ASSERT(0); + } + + ASSERT(0); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_Compare (NCDValRef val1, NCDValRef val2) +{ + NCDVal__AssertVal(val1); + NCDVal__AssertVal(val2); + + int type1 = NCDVal_Type(val1); + int type2 = NCDVal_Type(val2); + + if (type1 != type2) { + return (type1 > type2) - (type1 < type2); + } + + switch (type1) { + case NCDVAL_STRING: { + size_t len1 = NCDVal_StringLength(val1); + size_t len2 = NCDVal_StringLength(val2); + size_t min_len = len1 < len2 ? len1 : len2; + + int cmp = NCDVal_StringMemCmp(val1, val2, 0, 0, min_len); + if (cmp) { + return (cmp > 0) - (cmp < 0); + } + + return (len1 > len2) - (len1 < len2); + } break; + + case NCDVAL_LIST: { + size_t count1 = NCDVal_ListCount(val1); + size_t count2 = NCDVal_ListCount(val2); + size_t min_count = count1 < count2 ? count1 : count2; + + for (size_t i = 0; i < min_count; i++) { + NCDValRef ev1 = NCDVal_ListGet(val1, i); + NCDValRef ev2 = NCDVal_ListGet(val2, i); + + int cmp = NCDVal_Compare(ev1, ev2); + if (cmp) { + return cmp; + } + } + + return (count1 > count2) - (count1 < count2); + } break; + + case NCDVAL_MAP: { + NCDValMapElem e1 = NCDVal_MapOrderedFirst(val1); + NCDValMapElem e2 = NCDVal_MapOrderedFirst(val2); + + while (1) { + int inv1 = NCDVal_MapElemInvalid(e1); + int inv2 = NCDVal_MapElemInvalid(e2); + if (inv1 || inv2) { + return inv2 - inv1; + } + + NCDValRef key1 = NCDVal_MapElemKey(val1, e1); + NCDValRef key2 = NCDVal_MapElemKey(val2, e2); + + int cmp = NCDVal_Compare(key1, key2); + if (cmp) { + return cmp; + } + + NCDValRef value1 = NCDVal_MapElemVal(val1, e1); + NCDValRef value2 = NCDVal_MapElemVal(val2, e2); + + cmp = NCDVal_Compare(value1, value2); + if (cmp) { + return cmp; + } + + e1 = NCDVal_MapOrderedNext(val1, e1); + e2 = NCDVal_MapOrderedNext(val2, e2); + } + } break; + + case NCDVAL_PLACEHOLDER: { + int plid1 = NCDVal_PlaceholderId(val1); + int plid2 = NCDVal_PlaceholderId(val2); + + return (plid1 > plid2) - (plid1 < plid2); + } break; + + default: + ASSERT(0); + return 0; + } +} + +NCDValSafeRef NCDVal_ToSafe (NCDValRef val) +{ + NCDVal_Assert(val); + + NCDValSafeRef sval = {val.idx}; + return sval; +} + +NCDValRef NCDVal_FromSafe (NCDValMem *mem, NCDValSafeRef sval) +{ + NCDVal__AssertMem(mem); + ASSERT(sval.idx == -1 || (NCDVal__AssertValOnly(mem, sval.idx), 1)) + + NCDValRef val = {mem, sval.idx}; + return val; +} + +NCDValRef NCDVal_Moved (NCDValMem *mem, NCDValRef val) +{ + NCDVal__AssertMem(mem); + ASSERT(val.idx == -1 || (NCDVal__AssertValOnly(mem, val.idx), 1)) + + NCDValRef val2 = {mem, val.idx}; + return val2; +} + +int NCDVal_HasOnlyContinuousStrings (NCDValRef val) +{ + NCDVal__AssertVal(val); + + switch (NCDVal_Type(val)) { + case NCDVAL_STRING: { + if (!NCDVal_IsContinuousString(val)) { + return 0; + } + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(val); + for (size_t i = 0; i < count; i++) { + NCDValRef elem = NCDVal_ListGet(val, i); + if (!NCDVal_HasOnlyContinuousStrings(elem)) { + return 0; + } + } + } break; + + case NCDVAL_MAP: { + for (NCDValMapElem me = NCDVal_MapFirst(val); !NCDVal_MapElemInvalid(me); me = NCDVal_MapNext(val, me)) { + NCDValRef e_key = NCDVal_MapElemKey(val, me); + NCDValRef e_val = NCDVal_MapElemVal(val, me); + if (!NCDVal_HasOnlyContinuousStrings(e_key) || !NCDVal_HasOnlyContinuousStrings(e_val)) { + return 0; + } + } + } break; + + case NCDVAL_PLACEHOLDER: { + } break; + + default: + ASSERT(0); + } + + return 1; +} + +int NCDVal_IsString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_STRING; +} + +int NCDVal_IsContinuousString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + if (val.idx < -1) { + return 0; + } + + switch (get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx))) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + return 1; + default: + return 0; + } +} + +int NCDVal_IsStoredString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == STOREDSTRING_TYPE; +} + +int NCDVal_IsIdString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == IDSTRING_TYPE; +} + +int NCDVal_IsExternalString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == EXTERNALSTRING_TYPE; +} + +int NCDVal_IsComposedString (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return !(val.idx < -1) && get_internal_type(*(int *)NCDValMem__BufAt(val.mem, val.idx)) == COMPOSEDSTRING_TYPE; +} + +int NCDVal_IsStringNoNulls (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_STRING && !NCDVal_StringHasNulls(val); +} + +NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data) +{ + NCDVal__AssertMem(mem); + ASSERT(data) + NCDVal_AssertExternal(mem, data, strlen(data)); + + return NCDVal_NewStringBin(mem, (const uint8_t *)data, strlen(data)); +} + +NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len) +{ + NCDVal__AssertMem(mem); + ASSERT(len == 0 || data) + NCDVal_AssertExternal(mem, data, len); + + if (len > NCDVAL_MAXIDX - sizeof(struct NCDVal__string) - 1) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__string) + len + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + str_e->type = make_type(STOREDSTRING_TYPE, 0); + str_e->length = len; + if (len > 0) { + memcpy(str_e->data, data, len); + } + str_e->data[len] = '\0'; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len) +{ + NCDVal__AssertMem(mem); + + if (len > NCDVAL_MAXIDX - sizeof(struct NCDVal__string) - 1) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__string) + len + 1; + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__string)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__string *str_e = NCDValMem__BufAt(mem, idx); + str_e->type = make_type(STOREDSTRING_TYPE, 0); + str_e->length = len; + str_e->data[len] = '\0'; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewIdString (NCDValMem *mem, NCD_string_id_t string_id, NCDStringIndex *string_index) +{ + NCDVal__AssertMem(mem); + ASSERT(string_id >= 0) + ASSERT(string_index) + + NCDVal__idx size = sizeof(struct NCDVal__idstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__idstring)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(mem, idx); + ids_e->type = make_type(IDSTRING_TYPE, 0); + ids_e->string_id = string_id; + ids_e->string_index = string_index; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len, + BRefTarget *ref_target) +{ + NCDVal__AssertMem(mem); + ASSERT(data) + NCDVal_AssertExternal(mem, data, len); + + NCDVal__idx size = sizeof(struct NCDVal__externalstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__externalstring)); + if (idx < 0) { + goto fail; + } + + if (ref_target) { + if (!BRefTarget_Ref(ref_target)) { + goto fail; + } + } + + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(mem, idx); + exs_e->type = make_type(EXTERNALSTRING_TYPE, 0); + exs_e->data = data; + exs_e->length = len; + exs_e->ref.target = ref_target; + + if (ref_target) { + exs_e->ref.next = mem->first_ref; + mem->first_ref = idx + offsetof(struct NCDVal__externalstring, ref); + } + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +NCDValRef NCDVal_NewComposedString (NCDValMem *mem, NCDValComposedStringResource resource, size_t offset, size_t length) +{ + NCDVal__AssertMem(mem); + ASSERT(resource.func_getptr) + + NCDVal__idx size = sizeof(struct NCDVal__composedstring); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__composedstring)); + if (idx < 0) { + goto fail; + } + + if (resource.ref_target) { + if (!BRefTarget_Ref(resource.ref_target)) { + goto fail; + } + } + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(mem, idx); + cms_e->type = make_type(COMPOSEDSTRING_TYPE, 0); + cms_e->offset = offset; + cms_e->length = length; + cms_e->func_getptr = resource.func_getptr; + cms_e->user = resource.user; + cms_e->ref.target = resource.ref_target; + + if (resource.ref_target) { + cms_e->ref.next = mem->first_ref; + mem->first_ref = idx + offsetof(struct NCDVal__composedstring, ref); + } + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +const char * NCDVal_StringData (NCDValRef contstring) +{ + ASSERT(NCDVal_IsContinuousString(contstring)) + + void *ptr = NCDValMem__BufAt(contstring.mem, contstring.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return str_e->data; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + const char *value = NCDStringIndex_Value(ids_e->string_index, ids_e->string_id); + return value; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return exs_e->data; + } break; + + default: + ASSERT(0); + return NULL; + } +} + +size_t NCDVal_StringLength (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return str_e->length; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return NCDStringIndex_Length(ids_e->string_index, ids_e->string_id); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return exs_e->length; + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + return cms_e->length; + } break; + + default: + ASSERT(0); + return 0; + } +} + +b_cstring NCDValComposedStringResource_Cstring (NCDValComposedStringResource resource, size_t offset, size_t length) +{ + b_cstring cstr; + cstr.length = length; + cstr.func = NCDVal__composedstring_cstring_func; + cstr.user1.size = offset; + cstr.user2.fptr = (void (*) (void))resource.func_getptr; + cstr.user3.ptr = resource.user; + return cstr; +} + +b_cstring NCDVal_StringCstring (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + return b_cstring_make_buf(str_e->data, str_e->length); + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return b_cstring_make_buf(NCDStringIndex_Value(ids_e->string_index, ids_e->string_id), NCDStringIndex_Length(ids_e->string_index, ids_e->string_id)); + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + return b_cstring_make_buf(exs_e->data, exs_e->length); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + b_cstring cstr; + cstr.length = cms_e->length; + cstr.func = NCDVal__composedstring_cstring_func; + cstr.user1.size = cms_e->offset; + cstr.user2.fptr = (void (*) (void))cms_e->func_getptr; + cstr.user3.ptr = cms_e->user; + return cstr; + } break; + + default: { + ASSERT(0); + return b_cstring_make_empty(); + } break; + } +} + +int NCDVal_StringNullTerminate (NCDValRef string, NCDValNullTermString *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + out->data = str_e->data; + out->is_allocated = 0; + return 1; + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + out->data = (char *)NCDStringIndex_Value(ids_e->string_index, ids_e->string_id); + out->is_allocated = 0; + return 1; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + + char *copy = b_strdup_bin(exs_e->data, exs_e->length); + if (!copy) { + return 0; + } + + out->data = copy; + out->is_allocated = 1; + return 1; + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + size_t length = cms_e->length; + + if (length == SIZE_MAX) { + return 0; + } + + char *copy = BAlloc(length + 1); + if (!copy) { + return 0; + } + + NCDVal_StringCopyOut(string, 0, length, copy); + copy[length] = '\0'; + + out->data = copy; + out->is_allocated = 1; + return 1; + } break; + + default: + ASSERT(0); + return 0; + } +} + +NCDValNullTermString NCDValNullTermString_NewDummy (void) +{ + NCDValNullTermString nts; + nts.data = NULL; + nts.is_allocated = 0; + return nts; +} + +void NCDValNullTermString_Free (NCDValNullTermString *o) +{ + if (o->is_allocated) { + BFree(o->data); + } +} + +int NCDVal_StringContinuize (NCDValRef string, NCDValContString *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + if (NCDVal_IsContinuousString(string)) { + out->data = (char *)NCDVal_StringData(string); + out->is_allocated = 0; + return 1; + } + + size_t length = NCDVal_StringLength(string); + + char *data = BAlloc(length); + if (!data) { + return 0; + } + + NCDVal_StringCopyOut(string, 0, length, data); + + out->data = data; + out->is_allocated = 1; + return 1; +} + +NCDValContString NCDValContString_NewDummy (void) +{ + NCDValContString cts; + cts.data = NULL; + cts.is_allocated = 0; + return cts; +} + +void NCDValContString_Free (NCDValContString *o) +{ + if (o->is_allocated) { + BFree(o->data); + } +} + +void NCDVal_IdStringGet (NCDValRef idstring, NCD_string_id_t *out_string_id, + NCDStringIndex **out_string_index) +{ + ASSERT(NCDVal_IsIdString(idstring)) + ASSERT(out_string_id) + ASSERT(out_string_index) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + *out_string_id = ids_e->string_id; + *out_string_index = ids_e->string_index; +} + +NCD_string_id_t NCDVal_IdStringId (NCDValRef idstring) +{ + ASSERT(NCDVal_IsIdString(idstring)) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + return ids_e->string_id; +} + +NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring) +{ + ASSERT(NCDVal_IsIdString(idstring)) + + struct NCDVal__idstring *ids_e = NCDValMem__BufAt(idstring.mem, idstring.idx); + return ids_e->string_index; +} + +BRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring) +{ + ASSERT(NCDVal_IsExternalString(externalstring)) + + struct NCDVal__externalstring *exs_e = NCDValMem__BufAt(externalstring.mem, externalstring.idx); + return exs_e->ref.target; +} + +NCDValComposedStringResource NCDVal_ComposedStringResource (NCDValRef composedstring) +{ + ASSERT(NCDVal_IsComposedString(composedstring)) + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(composedstring.mem, composedstring.idx); + + NCDValComposedStringResource res; + res.func_getptr = cms_e->func_getptr; + res.user = cms_e->user; + res.ref_target = cms_e->ref.target; + + return res; +} + +size_t NCDVal_ComposedStringOffset (NCDValRef composedstring) +{ + ASSERT(NCDVal_IsComposedString(composedstring)) + + struct NCDVal__composedstring *cms_e = NCDValMem__BufAt(composedstring.mem, composedstring.idx); + + return cms_e->offset; +} + +int NCDVal_StringHasNulls (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + return NCDStringIndex_HasNulls(ids_e->string_index, ids_e->string_id); + } break; + + case STOREDSTRING_TYPE: + case EXTERNALSTRING_TYPE: { + const char *data = NCDVal_StringData(string); + size_t length = NCDVal_StringLength(string); + return !!memchr(data, '\0', length); + } break; + + case COMPOSEDSTRING_TYPE: { + b_cstring cstr = NCDVal_StringCstring(string); + return b_cstring_memchr(cstr, 0, cstr.length, '\0', NULL); + } break; + + default: + ASSERT(0); + return 0; + } +} + +int NCDVal_StringEquals (NCDValRef string, const char *data) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(data) + + size_t data_len = strlen(data); + + return NCDVal_StringLength(string) == data_len && NCDVal_StringRegionEquals(string, 0, data_len, data); +} + +int NCDVal_StringEqualsId (NCDValRef string, NCD_string_id_t string_id, + NCDStringIndex *string_index) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(string_id >= 0) + ASSERT(string_index) + + void *ptr = NCDValMem__BufAt(string.mem, string.idx); + + switch (get_internal_type(*(int *)ptr)) { + case STOREDSTRING_TYPE: { + struct NCDVal__string *str_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == str_e->length) && !memcmp(string_data, str_e->data, string_length); + } break; + + case IDSTRING_TYPE: { + struct NCDVal__idstring *ids_e = ptr; + ASSERT(ids_e->string_index == string_index) + return ids_e->string_id == string_id; + } break; + + case EXTERNALSTRING_TYPE: { + struct NCDVal__externalstring *exs_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == exs_e->length) && !memcmp(string_data, exs_e->data, string_length); + } break; + + case COMPOSEDSTRING_TYPE: { + struct NCDVal__composedstring *cms_e = ptr; + const char *string_data = NCDStringIndex_Value(string_index, string_id); + size_t string_length = NCDStringIndex_Length(string_index, string_id); + return (string_length == cms_e->length) && NCDVal_StringRegionEquals(string, 0, string_length, string_data); + } break; + + default: + ASSERT(0); + return 0; + } +} + +int NCDVal_StringMemCmp (NCDValRef string1, NCDValRef string2, size_t start1, size_t start2, size_t length) +{ + ASSERT(NCDVal_IsString(string1)) + ASSERT(NCDVal_IsString(string2)) + ASSERT(start1 <= NCDVal_StringLength(string1)) + ASSERT(start2 <= NCDVal_StringLength(string2)) + ASSERT(length <= NCDVal_StringLength(string1) - start1) + ASSERT(length <= NCDVal_StringLength(string2) - start2) + + if (NCDVal_IsContinuousString(string1) && NCDVal_IsContinuousString(string2)) { + return memcmp(NCDVal_StringData(string1) + start1, NCDVal_StringData(string2) + start2, length); + } + + b_cstring cstr1 = NCDVal_StringCstring(string1); + b_cstring cstr2 = NCDVal_StringCstring(string2); + return b_cstring_memcmp(cstr1, cstr2, start1, start2, length); +} + +void NCDVal_StringCopyOut (NCDValRef string, size_t start, size_t length, char *dst) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(start <= NCDVal_StringLength(string)) + ASSERT(length <= NCDVal_StringLength(string) - start) + + if (NCDVal_IsContinuousString(string)) { + memcpy(dst, NCDVal_StringData(string) + start, length); + return; + } + + b_cstring cstr = NCDVal_StringCstring(string); + b_cstring_copy_to_buf(cstr, start, length, dst); +} + +int NCDVal_StringRegionEquals (NCDValRef string, size_t start, size_t length, const char *data) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(start <= NCDVal_StringLength(string)) + ASSERT(length <= NCDVal_StringLength(string) - start) + + if (NCDVal_IsContinuousString(string)) { + return !memcmp(NCDVal_StringData(string) + start, data, length); + } + + b_cstring cstr = NCDVal_StringCstring(string); + return b_cstring_equals_buffer(cstr, start, length, data); +} + +int NCDVal_IsList (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_LIST; +} + +NCDValRef NCDVal_NewList (NCDValMem *mem, size_t maxcount) +{ + NCDVal__AssertMem(mem); + + if (maxcount > (NCDVAL_MAXIDX - sizeof(struct NCDVal__list)) / sizeof(NCDVal__idx)) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__list) + maxcount * sizeof(NCDVal__idx); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__list)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__list *list_e = NCDValMem__BufAt(mem, idx); + list_e->type = make_type(NCDVAL_LIST, 0); + list_e->maxcount = maxcount; + list_e->count = 0; + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_ListAppend (NCDValRef list, NCDValRef elem) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(NCDVal_ListCount(list) < NCDVal_ListMaxCount(list)) + ASSERT(elem.mem == list.mem) + NCDVal__AssertValOnly(list.mem, elem.idx); + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + int new_type = list_e->type; + if (!bump_depth(&new_type, NCDVal__Depth(elem))) { + return 0; + } + + if (NCDValMem__NeedRegisterLink(list.mem, elem.idx)) { + if (!NCDValMem__RegisterLink(list.mem, elem.idx, list.idx + offsetof(struct NCDVal__list, elem_indices) + list_e->count * sizeof(NCDVal__idx))) { + return 0; + } + list_e = NCDValMem__BufAt(list.mem, list.idx); + } + + list_e->type = new_type; + list_e->elem_indices[list_e->count++] = elem.idx; + + return 1; +} + +size_t NCDVal_ListCount (NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + return list_e->count; +} + +size_t NCDVal_ListMaxCount (NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + return list_e->maxcount; +} + +NCDValRef NCDVal_ListGet (NCDValRef list, size_t pos) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(pos < NCDVal_ListCount(list)) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + ASSERT(pos < list_e->count) + NCDVal__AssertValOnly(list.mem, list_e->elem_indices[pos]); + + return NCDVal__Ref(list.mem, list_e->elem_indices[pos]); +} + +int NCDVal_ListRead (NCDValRef list, int num, ...) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(num >= 0) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + if (num != list_e->count) { + return 0; + } + + va_list ap; + va_start(ap, num); + + for (int i = 0; i < num; i++) { + NCDValRef *dest = va_arg(ap, NCDValRef *); + *dest = NCDVal__Ref(list.mem, list_e->elem_indices[i]); + } + + va_end(ap); + + return 1; +} + +int NCDVal_ListReadHead (NCDValRef list, int num, ...) +{ + ASSERT(NCDVal_IsList(list)) + ASSERT(num >= 0) + + struct NCDVal__list *list_e = NCDValMem__BufAt(list.mem, list.idx); + + if (num > list_e->count) { + return 0; + } + + va_list ap; + va_start(ap, num); + + for (int i = 0; i < num; i++) { + NCDValRef *dest = va_arg(ap, NCDValRef *); + *dest = NCDVal__Ref(list.mem, list_e->elem_indices[i]); + } + + va_end(ap); + + return 1; +} + +int NCDVal_IsMap (NCDValRef val) +{ + NCDVal__AssertVal(val); + + return NCDVal_Type(val) == NCDVAL_MAP; +} + +NCDValRef NCDVal_NewMap (NCDValMem *mem, size_t maxcount) +{ + NCDVal__AssertMem(mem); + + if (maxcount > (NCDVAL_MAXIDX - sizeof(struct NCDVal__map)) / sizeof(struct NCDVal__mapelem)) { + goto fail; + } + + NCDVal__idx size = sizeof(struct NCDVal__map) + maxcount * sizeof(struct NCDVal__mapelem); + NCDVal__idx idx = NCDValMem__Alloc(mem, size, __alignof(struct NCDVal__map)); + if (idx < 0) { + goto fail; + } + + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, idx); + map_e->type = make_type(NCDVAL_MAP, 0); + map_e->maxcount = maxcount; + map_e->count = 0; + NCDVal__MapTree_Init(&map_e->tree); + + return NCDVal__Ref(mem, idx); + +fail: + return NCDVal_NewInvalid(); +} + +int NCDVal_MapInsert (NCDValRef map, NCDValRef key, NCDValRef val, int *out_inserted) +{ + ASSERT(NCDVal_IsMap(map)) + ASSERT(NCDVal_MapCount(map) < NCDVal_MapMaxCount(map)) + ASSERT(key.mem == map.mem) + ASSERT(val.mem == map.mem) + NCDVal__AssertValOnly(map.mem, key.idx); + NCDVal__AssertValOnly(map.mem, val.idx); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + int new_type = map_e->type; + if (!bump_depth(&new_type, NCDVal__Depth(key)) || !bump_depth(&new_type, NCDVal__Depth(val))) { + goto fail0; + } + + NCDVal__idx elemidx = NCDVal__MapElemIdx(map.idx, map_e->count); + + if (NCDValMem__NeedRegisterLink(map.mem, key.idx)) { + if (!NCDValMem__RegisterLink(map.mem, key.idx, elemidx + offsetof(struct NCDVal__mapelem, key_idx))) { + goto fail0; + } + map_e = NCDValMem__BufAt(map.mem, map.idx); + } + + if (NCDValMem__NeedRegisterLink(map.mem, val.idx)) { + if (!NCDValMem__RegisterLink(map.mem, val.idx, elemidx + offsetof(struct NCDVal__mapelem, val_idx))) { + goto fail1; + } + map_e = NCDValMem__BufAt(map.mem, map.idx); + } + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, elemidx); + ASSERT(me_e == &map_e->elems[map_e->count]) + me_e->key_idx = key.idx; + me_e->val_idx = val.idx; + + int res = NCDVal__MapTree_Insert(&map_e->tree, map.mem, NCDVal__MapTreeDeref(map.mem, elemidx), NULL); + if (!res) { + if (out_inserted) { + *out_inserted = 0; + } + return 1; + } + + map_e->type = new_type; + map_e->count++; + + if (out_inserted) { + *out_inserted = 1; + } + return 1; + +fail1: + if (NCDValMem__NeedRegisterLink(map.mem, key.idx)) { + NCDValMem__PopLastRegisteredLink(map.mem); + } +fail0: + return 0; +} + +size_t NCDVal_MapCount (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + return map_e->count; +} + +size_t NCDVal_MapMaxCount (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + return map_e->maxcount; +} + +int NCDVal_MapElemInvalid (NCDValMapElem me) +{ + ASSERT(me.elemidx >= 0 || me.elemidx == -1) + + return me.elemidx < 0; +} + +NCDValMapElem NCDVal_MapFirst (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + if (map_e->count == 0) { + return NCDVal__MapElem(-1); + } + + NCDVal__idx elemidx = NCDVal__MapElemIdx(map.idx, 0); + NCDVal__MapAssertElemOnly(map, elemidx); + + return NCDVal__MapElem(elemidx); +} + +NCDValMapElem NCDVal_MapNext (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + ASSERT(map_e->count > 0) + + NCDVal__idx last_elemidx = NCDVal__MapElemIdx(map.idx, map_e->count - 1); + ASSERT(me.elemidx <= last_elemidx) + + if (me.elemidx == last_elemidx) { + return NCDVal__MapElem(-1); + } + + NCDVal__idx elemidx = me.elemidx + sizeof(struct NCDVal__mapelem); + NCDVal__MapAssertElemOnly(map, elemidx); + + return NCDVal__MapElem(elemidx); +} + +NCDValMapElem NCDVal_MapOrderedFirst (NCDValRef map) +{ + ASSERT(NCDVal_IsMap(map)) + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_GetFirst(&map_e->tree, map.mem); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValMapElem NCDVal_MapOrderedNext (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_GetNext(&map_e->tree, map.mem, NCDVal__MapTreeDeref(map.mem, me.elemidx)); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValRef NCDVal_MapElemKey (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, me.elemidx); + + return NCDVal__Ref(map.mem, me_e->key_idx); +} + +NCDValRef NCDVal_MapElemVal (NCDValRef map, NCDValMapElem me) +{ + NCDVal__MapAssertElem(map, me); + + struct NCDVal__mapelem *me_e = NCDValMem__BufAt(map.mem, me.elemidx); + + return NCDVal__Ref(map.mem, me_e->val_idx); +} + +NCDValMapElem NCDVal_MapFindKey (NCDValRef map, NCDValRef key) +{ + ASSERT(NCDVal_IsMap(map)) + NCDVal__AssertVal(key); + + struct NCDVal__map *map_e = NCDValMem__BufAt(map.mem, map.idx); + + NCDVal__MapTreeRef ref = NCDVal__MapTree_LookupExact(&map_e->tree, map.mem, key); + ASSERT(ref.link == -1 || (NCDVal__MapAssertElemOnly(map, ref.link), 1)) + + return NCDVal__MapElem(ref.link); +} + +NCDValRef NCDVal_MapGetValue (NCDValRef map, const char *key_str) +{ + ASSERT(NCDVal_IsMap(map)) + ASSERT(key_str) + + NCDValMem mem; + mem.buf = NULL; + mem.size = NCDVAL_FASTBUF_SIZE; + mem.used = sizeof(struct NCDVal__externalstring); + mem.first_ref = -1; + + struct NCDVal__externalstring *exs_e = (void *)mem.fastbuf; + exs_e->type = make_type(EXTERNALSTRING_TYPE, 0); + exs_e->data = key_str; + exs_e->length = strlen(key_str); + exs_e->ref.target = NULL; + + NCDValRef key = NCDVal__Ref(&mem, 0); + + NCDValMapElem elem = NCDVal_MapFindKey(map, key); + if (NCDVal_MapElemInvalid(elem)) { + return NCDVal_NewInvalid(); + } + + return NCDVal_MapElemVal(map, elem); +} + +static void replaceprog_build_recurser (NCDValMem *mem, NCDVal__idx idx, size_t *out_num_instr, NCDValReplaceProg *prog) +{ + ASSERT(idx >= 0) + NCDVal__AssertValOnly(mem, idx); + ASSERT(out_num_instr) + + *out_num_instr = 0; + + void *ptr = NCDValMem__BufAt(mem, idx); + + struct NCDVal__instr instr; + + switch (get_internal_type(*((int *)(ptr)))) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + } break; + + case NCDVAL_LIST: { + struct NCDVal__list *list_e = ptr; + + for (NCDVal__idx i = 0; i < list_e->count; i++) { + int elem_changed = 0; + + if (list_e->elem_indices[i] < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = list_e->elem_indices[i] - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + elem_changed = 1; + } else { + size_t elem_num_instr; + replaceprog_build_recurser(mem, list_e->elem_indices[i], &elem_num_instr, prog); + (*out_num_instr) += elem_num_instr; + if (elem_num_instr > 0) { + elem_changed = 1; + } + } + + if (elem_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__list, elem_indices) + i * sizeof(NCDVal__idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + } + } break; + + case NCDVAL_MAP: { + struct NCDVal__map *map_e = ptr; + + for (NCDVal__idx i = 0; i < map_e->count; i++) { + int key_changed = 0; + int val_changed = 0; + + if (map_e->elems[i].key_idx < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = map_e->elems[i].key_idx - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, key_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + key_changed = 1; + } else { + size_t key_num_instr; + replaceprog_build_recurser(mem, map_e->elems[i].key_idx, &key_num_instr, prog); + (*out_num_instr) += key_num_instr; + if (key_num_instr > 0) { + key_changed = 1; + } + } + + if (map_e->elems[i].val_idx < -1) { + if (prog) { + instr.type = NCDVAL_INSTR_PLACEHOLDER; + instr.placeholder.plid = map_e->elems[i].val_idx - NCDVAL_MINIDX; + instr.placeholder.plidx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, val_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + val_changed = 1; + } else { + size_t val_num_instr; + replaceprog_build_recurser(mem, map_e->elems[i].val_idx, &val_num_instr, prog); + (*out_num_instr) += val_num_instr; + if (val_num_instr > 0) { + val_changed = 1; + } + } + + if (key_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_REINSERT; + instr.reinsert.mapidx = idx; + instr.reinsert.elempos = i; + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, key_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + + if (val_changed) { + if (prog) { + instr.type = NCDVAL_INSTR_BUMPDEPTH; + instr.bumpdepth.parent_idx = idx; + instr.bumpdepth.child_idx_idx = idx + offsetof(struct NCDVal__map, elems) + i * sizeof(struct NCDVal__mapelem) + offsetof(struct NCDVal__mapelem, val_idx); + prog->instrs[prog->num_instrs++] = instr; + } + (*out_num_instr)++; + } + } + } break; + + default: ASSERT(0); + } +} + +int NCDValReplaceProg_Init (NCDValReplaceProg *o, NCDValRef val) +{ + NCDVal__AssertVal(val); + ASSERT(!NCDVal_IsPlaceholder(val)) + + size_t num_instrs; + replaceprog_build_recurser(val.mem, val.idx, &num_instrs, NULL); + + if (!(o->instrs = BAllocArray(num_instrs, sizeof(o->instrs[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + return 0; + } + + o->num_instrs = 0; + + size_t num_instrs2; + replaceprog_build_recurser(val.mem, val.idx, &num_instrs2, o); + + ASSERT(num_instrs2 == num_instrs) + ASSERT(o->num_instrs == num_instrs) + + return 1; +} + +void NCDValReplaceProg_Free (NCDValReplaceProg *o) +{ + BFree(o->instrs); +} + +int NCDValReplaceProg_Execute (NCDValReplaceProg prog, NCDValMem *mem, NCDVal_replace_func replace, void *arg) +{ + NCDVal__AssertMem(mem); + ASSERT(replace) + + for (size_t i = 0; i < prog.num_instrs; i++) { + struct NCDVal__instr instr = prog.instrs[i]; + + switch (instr.type) { + case NCDVAL_INSTR_PLACEHOLDER: { +#ifndef NDEBUG + NCDVal__idx *check_plptr = NCDValMem__BufAt(mem, instr.placeholder.plidx); + ASSERT(*check_plptr < -1) + ASSERT(*check_plptr - NCDVAL_MINIDX == instr.placeholder.plid) +#endif + NCDValRef repval; + if (!replace(arg, instr.placeholder.plid, mem, &repval) || NCDVal_IsInvalid(repval)) { + return 0; + } + ASSERT(repval.mem == mem) + + if (NCDValMem__NeedRegisterLink(mem, repval.idx)) { + NCDValMem__RegisterLink(mem, repval.idx, instr.placeholder.plidx); + } + + NCDVal__idx *plptr = NCDValMem__BufAt(mem, instr.placeholder.plidx); + *plptr = repval.idx; + } break; + + case NCDVAL_INSTR_REINSERT: { + NCDVal__AssertValOnly(mem, instr.reinsert.mapidx); + struct NCDVal__map *map_e = NCDValMem__BufAt(mem, instr.reinsert.mapidx); + ASSERT(get_internal_type(map_e->type) == NCDVAL_MAP) + ASSERT(instr.reinsert.elempos >= 0) + ASSERT(instr.reinsert.elempos < map_e->count) + + NCDVal__MapTreeRef ref = {&map_e->elems[instr.reinsert.elempos], NCDVal__MapElemIdx(instr.reinsert.mapidx, instr.reinsert.elempos)}; + NCDVal__MapTree_Remove(&map_e->tree, mem, ref); + if (!NCDVal__MapTree_Insert(&map_e->tree, mem, ref, NULL)) { + BLog(BLOG_ERROR, "duplicate key in map"); + return 0; + } + } break; + + case NCDVAL_INSTR_BUMPDEPTH: { + NCDVal__AssertValOnly(mem, instr.bumpdepth.parent_idx); + int *parent_type_ptr = NCDValMem__BufAt(mem, instr.bumpdepth.parent_idx); + + NCDVal__idx *child_type_idx_ptr = NCDValMem__BufAt(mem, instr.bumpdepth.child_idx_idx); + NCDVal__AssertValOnly(mem, *child_type_idx_ptr); + int *child_type_ptr = NCDValMem__BufAt(mem, *child_type_idx_ptr); + + if (!bump_depth(parent_type_ptr, get_depth(*child_type_ptr))) { + BLog(BLOG_ERROR, "depth limit exceeded"); + return 0; + } + } break; + + default: ASSERT(0); + } + } + + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDVal.h b/external/badvpn_dns/ncd/NCDVal.h new file mode 100644 index 00000000..26154dae --- /dev/null +++ b/external/badvpn_dns/ncd/NCDVal.h @@ -0,0 +1,857 @@ +/** + * @file NCDVal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVAL_H +#define BADVPN_NCDVAL_H + +#include +#include + +#include +#include +#include +#include +#include + +// these are implementation details. The interface is defined below. + +#define NCDVAL_FASTBUF_SIZE 64 +#define NCDVAL_FIRST_SIZE 256 +#define NCDVAL_MAX_DEPTH 32 + +#define NCDVAL_MAXIDX INT_MAX +#define NCDVAL_MINIDX INT_MIN + +typedef int NCDVal__idx; + +struct NCDVal__ref { + NCDVal__idx next; + BRefTarget *target; +}; + +struct NCDVal__string { + int type; + NCDVal__idx length; + char data[]; +}; + +struct NCDVal__list { + int type; + NCDVal__idx maxcount; + NCDVal__idx count; + NCDVal__idx elem_indices[]; +}; + +struct NCDVal__mapelem { + NCDVal__idx key_idx; + NCDVal__idx val_idx; + NCDVal__idx tree_child[2]; + NCDVal__idx tree_parent; + int8_t tree_balance; +}; + +struct NCDVal__idstring { + int type; + NCD_string_id_t string_id; + NCDStringIndex *string_index; +}; + +struct NCDVal__externalstring { + int type; + const char *data; + size_t length; + struct NCDVal__ref ref; +}; + +struct NCDVal__composedstring { + int type; + size_t offset; + size_t length; + void (*func_getptr) (void *, size_t, const char **, size_t *); + void *user; + struct NCDVal__ref ref; +}; + +struct NCDVal__cms_link { + NCDVal__idx link_idx; + NCDVal__idx next_cms_link; +}; + +typedef struct { + char *buf; + NCDVal__idx size; + NCDVal__idx used; + NCDVal__idx first_ref; + NCDVal__idx first_cms_link; + union { + char fastbuf[NCDVAL_FASTBUF_SIZE]; + struct NCDVal__ref align_ref; + struct NCDVal__string align_string; + struct NCDVal__list align_list; + struct NCDVal__mapelem align_mapelem; + struct NCDVal__idstring align_idstring; + struct NCDVal__externalstring align_externalstring; + struct NCDVal__composedstring align_composedstring; + struct NCDVal__cms_link align_cms_link; + }; +} NCDValMem; + +typedef struct { + NCDValMem *mem; + NCDVal__idx idx; +} NCDValRef; + +typedef struct { + NCDVal__idx idx; +} NCDValSafeRef; + +typedef struct NCDVal__mapelem NCDVal__maptree_entry; +typedef NCDValMem *NCDVal__maptree_arg; + +#include "NCDVal_maptree.h" +#include + +struct NCDVal__map { + int type; + NCDVal__idx maxcount; + NCDVal__idx count; + NCDVal__MapTree tree; + struct NCDVal__mapelem elems[]; +}; + +typedef struct { + NCDVal__idx elemidx; +} NCDValMapElem; + +#define NCDVAL_INSTR_PLACEHOLDER 0 +#define NCDVAL_INSTR_REINSERT 1 +#define NCDVAL_INSTR_BUMPDEPTH 2 + +struct NCDVal__instr { + int type; + union { + struct { + NCDVal__idx plid; + NCDVal__idx plidx; + } placeholder; + struct { + NCDVal__idx mapidx; + NCDVal__idx elempos; + } reinsert; + struct { + NCDVal__idx parent_idx; + NCDVal__idx child_idx_idx; + } bumpdepth; + }; +}; + +typedef struct { + struct NCDVal__instr *instrs; + size_t num_instrs; +} NCDValReplaceProg; + +typedef struct { + char *data; + int is_allocated; +} NCDValNullTermString; + +typedef struct { + char *data; + int is_allocated; +} NCDValContString; + +// + +#define NCDVAL_STRING 1 +#define NCDVAL_LIST 2 +#define NCDVAL_MAP 3 +#define NCDVAL_PLACEHOLDER 4 + +/** + * Initializes a value memory object. + * A value memory object holds memory for value structures. Values within + * the memory are referenced using {@link NCDValRef} objects, which point + * to values within memory objects. + * + * Values may be added to a memory object using functions such as + * {@link NCDVal_NewString}, {@link NCDVal_NewList} and {@link NCDVal_NewMap}, + * and {@link NCDVal_NewCopy}, which return references to the new values within + * the memory object. + * + * It is not possible to remove values from the memory object, or modify existing + * values other than adding elements to pre-allocated slots in lists and maps. + * Once a value is added, it will consume memory as long as its memory object + * exists. This is by design - this code is intended and optimized for constructing + * and passing around values, not for operating on them in place. In fact, al + * values within a memory object are stored in a single memory buffer, as an + * embedded data structure with relativepointers. For example, map values use an + * embedded AVL tree. + */ +void NCDValMem_Init (NCDValMem *o); + +/** + * Frees a value memory object. + * All values within the memory object cease to exist, and any {@link NCDValRef} + * object pointing to them must no longer be used. + */ +void NCDValMem_Free (NCDValMem *o); + +/** + * Initializes the memory object to be a copy of an existing memory object. + * Value references from the original may be used if they are first turned + * to {@link NCDValSafeRef} using {@link NCDVal_ToSafe} and back to + * {@link NCDValRef} using {@link NCDVal_FromSafe} with the new memory object + * specified. Alternatively, {@link NCDVal_Moved} can be used. + * Returns 1 on success and 0 on failure. + */ +int NCDValMem_InitCopy (NCDValMem *o, NCDValMem *other) WARN_UNUSED; + +/** + * For each internal link (e.g. list element) to a ComposedString in the memory + * object, copies the ComposedString to some kind ContinuousString, and updates + * the link to point to the new ContinuousString. + * Additionally, if *\a root_val points to a ComposedString, copies it to a new + * ContinuousString and updates *\a root_val to point to it. + * \a root_val must be non-NULL and *\a root_val must not be an invalid value + * reference. + * Returns 1 on success and 0 on failure. On failure, some strings may have + * been converted, but the memory object is left in a consistent state. + */ +int NCDValMem_ConvertNonContinuousStrings (NCDValMem *o, NCDValRef *root_val) WARN_UNUSED; + +/** + * Does nothing. + * The value reference object must either point to a valid value within a valid + * memory object, or must be an invalid reference (most functions operating on + * {@link NCDValRef} implicitly require that). + */ +void NCDVal_Assert (NCDValRef val); + +/** + * Determines if a value reference is invalid. + */ +int NCDVal_IsInvalid (NCDValRef val); + +/** + * Determines if a value is a placeholder value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsPlaceholder (NCDValRef val); + +/** + * Returns the type of the value reference, which must not be an invalid reference. + * Possible values are NCDVAL_STRING, NCDVAL_LIST, NCDVAL_MAP and NCDVAL_PLACEHOLDER. + * The placeholder type is only used internally in the interpreter for argument + * resolution, and is never seen by modules; see {@link NCDVal_NewPlaceholder}. + */ +int NCDVal_Type (NCDValRef val); + +/** + * Returns an invalid reference. + * An invalid reference must not be passed to any function here, except: + * {@link NCDVal_Assert}, {@link NCDVal_IsInvalid}, {@link NCDVal_ToSafe}, + * {@link NCDVal_FromSafe}, {@link NCDVal_Moved}. + */ +NCDValRef NCDVal_NewInvalid (void); + +/** + * Returns a new placeholder value reference. A placeholder value is a valid value + * containing an integer placeholder identifier. + * This always succeeds; however, the caller must ensure the identifier is + * non-negative and satisfies (NCDVAL_MINIDX + plid < -1). + * + * The placeholder type is only used internally in the interpreter for argument + * resolution, and is never seen by modules. Also see {@link NCDPlaceholderDb}. + */ +NCDValRef NCDVal_NewPlaceholder (NCDValMem *mem, int plid); + +/** + * Returns the indentifier of a placeholder value. + * The value reference must point to a placeholder value. + */ +int NCDVal_PlaceholderId (NCDValRef val); + +/** + * Copies a value into the specified memory object. The source + * must not be an invalid reference, however it may reside in any memory + * object (including 'mem'). + * Returns a reference to the copied value. On out of memory, returns + * an invalid reference. + */ +NCDValRef NCDVal_NewCopy (NCDValMem *mem, NCDValRef val); + +/** + * Compares two values, both of which must not be invalid references. + * Returns -1, 0 or 1. + */ +int NCDVal_Compare (NCDValRef val1, NCDValRef val2); + +/** + * Converts a value reference to a safe referece format, which remains valid + * if the memory object is moved (safe references do not contain a pointer + * to the memory object, unlike {@link NCDValRef} references). + */ +NCDValSafeRef NCDVal_ToSafe (NCDValRef val); + +/** + * Converts a safe value reference to a normal value reference. + * This should be used to recover references from safe references + * after the memory object is moved. + */ +NCDValRef NCDVal_FromSafe (NCDValMem *mem, NCDValSafeRef sval); + +/** + * Fixes a value reference after its memory object was moved. + */ +NCDValRef NCDVal_Moved (NCDValMem *mem, NCDValRef val); + +/** + * Determines if all strings within this value are ContinuousString's, + * by recusively walking the entire value. + * If all strings are ContinuousString's, returns 1; if there is at least + * one string which is not a ContinuousString, returns 0. + * The value reference must not be an invalid reference. + */ +int NCDVal_HasOnlyContinuousStrings (NCDValRef val); + +/** + * Determines if the value implements the String interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsString (NCDValRef val); + +/** + * Determines if the value implements the ContinuousString interface. + * A ContinuousString also implements the String interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsContinuousString (NCDValRef val); + +/** + * Determines if the value is a StoredString. + * A StoredString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsStoredString (NCDValRef val); + +/** + * Determines if the value is an IdString. See {@link NCDVal_NewIdString} + * for details. + * An IdString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsIdString (NCDValRef val); + +/** + * Determines if a value is an ExternalString. + * See {@link NCDVal_NewExternalString} for details. + * An ExternalString implements the ContinuousString interface. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsExternalString (NCDValRef val); + +/** + * Determines if a value is a ComposedString. + * A ComposedString implements the String interface. + */ +int NCDVal_IsComposedString (NCDValRef val); + +/** + * Determines if a value is a String which contains no null bytes. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsStringNoNulls (NCDValRef val); + +/** + * Equivalent to NCDVal_NewStringBin(mem, data, strlen(data)). + */ +NCDValRef NCDVal_NewString (NCDValMem *mem, const char *data); + +/** + * Builds a new StoredString. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * WARNING: The buffer passed must NOT be part of any value in the + * memory object specified. In particular, you may NOT use this + * function to copy a string that resides in the same memory object. + * + * A StoredString is a kind of ContinuousString which is represented directly in the + * value memory object. + */ +NCDValRef NCDVal_NewStringBin (NCDValMem *mem, const uint8_t *data, size_t len); + +/** + * Builds a new StoredString of the given length with undefined contents. + * You can define the contents of the string later by copying to the address + * returned by {@link NCDVal_StringData}. + */ +NCDValRef NCDVal_NewStringUninitialized (NCDValMem *mem, size_t len); + +/** + * Builds a new IdString. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * + * An IdString is a kind of ContinuousString which is represented efficiently as a string + * identifier via {@link NCDStringIndex}. + */ +NCDValRef NCDVal_NewIdString (NCDValMem *mem, NCD_string_id_t string_id, + NCDStringIndex *string_index); + +/** + * Builds a new ExternalString, pointing to the given external data. A reference to + * the external data is taken using {@link BRefTarget}, unless 'ref_target' is + * NULL. The data must not change while this value exists. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + * + * An ExternalString is a kind of ContinuousString where the actual string contents are + * stored outside of the value memory object. + */ +NCDValRef NCDVal_NewExternalString (NCDValMem *mem, const char *data, size_t len, + BRefTarget *ref_target); + +/** + * Callback function which is called for ComposedString's to access the underlying string resource. + * \a user is whatever was passed to 'resource.user' in {@link NCDVal_NewComposedString}. + * \a offset is the offset from the beginning of the string exposed by the resource; it will be + * >= 'offset' and < 'offset' + 'length' as given to NCDVal_NewComposedString. + * This callback must set *\a out_data and *\a out_length to represent a continuous (sub-)region + * of the string that starts at the byte at index \a offset. The pointed-to data must remain + * valid and unchanged until all references to the string resource are released. + * \a *out_data must be set to non-NULL and *\a out_length must be set to greater than zero, + * since the conditions above imply that there is at least one byte available from \a offset. + */ +typedef void (*NCDVal_ComposedString_func_getptr) (void *user, size_t offset, const char **out_data, size_t *out_length); + +/** + * Structure representing a string resource used by ComposedString's, + * to simplify {@link NCDVal_NewComposedString} and {@link NCDVal_ComposedStringResource}. + */ +typedef struct { + NCDVal_ComposedString_func_getptr func_getptr; + void *user; + BRefTarget *ref_target; +} NCDValComposedStringResource; + +/** + * Returns a cstring referencing a range within a {@link NCDValComposedStringResource}. + * \a offset and \a length specify the range within the resource which the returned + * cstring will reference. To reference the contents of a ComposedString, use: + * - resource = NCDVal_ComposedStringResource(composedstring), + * - offset = NCDVal_ComposedStringOffset(composedstring), + * - length = NCDVal_StringLength(composedstring). + * + * The returned cstring is valid as long as the resource is not released. Note that + * a reference to resource.ref_target may need to be taken to ensure the resource + * is not released while it is being referenced by the returned cstring (unless + * resource.ref_target is NULL). + */ +b_cstring NCDValComposedStringResource_Cstring (NCDValComposedStringResource resource, size_t offset, size_t length); + +/** + * Builds a new ComposedString from a string resource. + * A reference to the underlying string resource via the {@link BRefTarget} object + * specified in 'resource.ref_target'. + * + * A ComposedString is a kind of String with an abstract representation exposed via the + * {@link NCDVal_ComposedString_func_getptr} callback. + */ +NCDValRef NCDVal_NewComposedString (NCDValMem *mem, NCDValComposedStringResource resource, size_t offset, size_t length); + +/** + * Returns a pointer to the data of a ContinuousString. + * WARNING: the string data may not be null-terminated. To get a null-terminated + * version, use {@link NCDVal_StringNullTerminate}. + * The value reference must point to a ContinuousString. + */ +const char * NCDVal_StringData (NCDValRef contstring); + +/** + * Returns the length of a String. + * The value reference must point to a String. + */ +size_t NCDVal_StringLength (NCDValRef string); + +/** + * Returns a {@link b_cstring} interface to the given string value. + * The returned cstring is valid as long as the memory object exists. + * However, if the memory object is moved or copied, the cstring is + * invalid in the new or moved (respectively) memory object. + */ +b_cstring NCDVal_StringCstring (NCDValRef string); + +/** + * Produces a null-terminated continuous version of a String. On success, the result is + * stored into an {@link NCDValNullTermString} structure, and the null-terminated + * string is available via its 'data' member. This function may either simply pass + * through the data pointer (if the string is known to be continuous and null-terminated) or + * produce a null-terminated dynamically allocated copy. + * On success, {@link NCDValNullTermString_Free} should be called to release any allocated + * memory when the null-terminated string is no longer needed. This must be called before + * the memory object is freed, because it may point to data inside the memory object. + * It is guaranteed that *out is not modified on failure. + * Returns 1 on success and 0 on failure. + */ +int NCDVal_StringNullTerminate (NCDValRef string, NCDValNullTermString *out) WARN_UNUSED; + +/** + * Returns a dummy {@link NCDValNullTermString} which can be freed using + * {@link NCDValNullTermString_Free}, but need not be. + */ +NCDValNullTermString NCDValNullTermString_NewDummy (void); + +/** + * Releases any memory which was dynamically allocated by {@link NCDVal_StringNullTerminate} + * to null-terminate a string. + */ +void NCDValNullTermString_Free (NCDValNullTermString *o); + +/** + * Produces a continuous version of a String. On success, the result is stored into an + * {@link NCDValContString} structure, and the continuous string is available via its + * 'data' member. This function may either simply pass through the data pointer (if the + * string is known to be continuous) or produce a continuous dynamically allocated copy. + * On success, {@link NCDValContString_Free} should be called to release any allocated + * memory when the continuous string is no longer needed. This must be called before + * the memory object is freed, because it may point to data inside the memory object. + * It is guaranteed that *out is not modified on failure. + * Returns 1 on success and 0 on failure. + */ +int NCDVal_StringContinuize (NCDValRef string, NCDValContString *out) WARN_UNUSED; + +/** + * Returns a dummy {@link NCDValContString} which can be freed using + * {@link NCDValContString_Free}, but need not be. + */ +NCDValContString NCDValContString_NewDummy (void); + +/** + * Releases any memory which was dynamically allocated by {@link NCDVal_StringContinuize} + * to continuize a string. + */ +void NCDValContString_Free (NCDValContString *o); + +/** + * Returns the string ID and the string index of an IdString. + * Both the \a out_string_id and \a out_string_index pointers must be non-NULL. + */ +void NCDVal_IdStringGet (NCDValRef idstring, NCD_string_id_t *out_string_id, + NCDStringIndex **out_string_index); + +/** + * Returns the string ID of an IdString. + */ +NCD_string_id_t NCDVal_IdStringId (NCDValRef idstring); + +/** + * Returns the string index of an IdString. + */ +NCDStringIndex * NCDVal_IdStringStringIndex (NCDValRef idstring); + +/** + * Returns the reference target of an ExternalString. This may be NULL + * if the external string is not associated with a reference target. + */ +BRefTarget * NCDVal_ExternalStringTarget (NCDValRef externalstring); + +/** + * Returns the underlying string resource of a ComposedString. + */ +NCDValComposedStringResource NCDVal_ComposedStringResource (NCDValRef composedstring); + +/** + * Returns the resource offset of a ComposedString. + */ +size_t NCDVal_ComposedStringOffset (NCDValRef composedstring); + +/** + * Determines if the String has any null bytes in its contents. + */ +int NCDVal_StringHasNulls (NCDValRef string); + +/** + * Determines if the String value is equal to the given null-terminated + * string. + * The value reference must point to a String value. + */ +int NCDVal_StringEquals (NCDValRef string, const char *data); + +/** + * Determines if the String is equal to the given string represented + * by an {@link NCDStringIndex} identifier. + * NOTE: \a string_index must be equal to the string_index of every ID-string + * that exist within this memory object. + */ +int NCDVal_StringEqualsId (NCDValRef string, NCD_string_id_t string_id, + NCDStringIndex *string_index); + +/** + * Compares two String's in a manner similar to memcmp(). + * The startN and length arguments must refer to a valid region within + * stringN, i.e. startN + length <= length_of_stringN must hold. + */ +int NCDVal_StringMemCmp (NCDValRef string1, NCDValRef string2, size_t start1, size_t start2, size_t length); + +/** + * Copies a part of a String to a buffer. + * \a start and \a length must refer to a valid region within the string, + * i.e. start + length <= length_of_string must hold. + */ +void NCDVal_StringCopyOut (NCDValRef string, size_t start, size_t length, char *dst); + +/** + * Determines if a part of a String is equal to the \a length bytes in \a data. + * \a start and \a length must refer to a valid region within the string, + * i.e. start + length <= length_of_string must hold. + */ +int NCDVal_StringRegionEquals (NCDValRef string, size_t start, size_t length, const char *data); + +/** + * Determines if a value is a list value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsList (NCDValRef val); + +/** + * Builds a new list value. The 'maxcount' argument specifies how + * many element slots to preallocate. Not more than that many + * elements may be appended to the list using {@link NCDVal_ListAppend}. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + */ +NCDValRef NCDVal_NewList (NCDValMem *mem, size_t maxcount); + +/** + * Appends a value to to the list value. + * The 'list' reference must point to a list value, and the + * 'elem' reference must be non-invalid and point to a value within + * the same memory object as the list. + * Inserting a value into a list does not in any way change it; + * internally, the list only points to it. + * You must not modify the element after it has been inserted into the + * list. + * Returns 1 on success and 0 on failure (depth limit exceeded). + */ +int NCDVal_ListAppend (NCDValRef list, NCDValRef elem) WARN_UNUSED; + +/** + * Returns the number of elements in a list value, i.e. the number + * of times {@link NCDVal_ListAppend} was called. + * The 'list' reference must point to a list value. + */ +size_t NCDVal_ListCount (NCDValRef list); + +/** + * Returns the maximum number of elements a list value may contain, + * i.e. the 'maxcount' argument to {@link NCDVal_NewList}. + * The 'list' reference must point to a list value. + */ +size_t NCDVal_ListMaxCount (NCDValRef list); + +/** + * Returns a reference to the value at the given position 'pos' in a list, + * starting with zero. + * The 'list' reference must point to a list value. + * The position 'pos' must refer to an existing element, i.e. + * pos < NCDVal_ListCount(). + */ +NCDValRef NCDVal_ListGet (NCDValRef list, size_t pos); + +/** + * Returns references to elements within a list by writing them + * via (NCDValRef *) variable arguments. + * If 'num' == NCDVal_ListCount(), succeeds, returing 1 and writing 'num' + * references, as mentioned. + * If 'num' != NCDVal_ListCount(), fails, returning 0, without writing any + * references + */ +int NCDVal_ListRead (NCDValRef list, int num, ...); + +/** + * Like {@link NCDVal_ListRead}, but the list can contain more than 'num' + * elements. + */ +int NCDVal_ListReadHead (NCDValRef list, int num, ...); + +/** + * Determines if a value is a map value. + * The value reference must not be an invalid reference. + */ +int NCDVal_IsMap (NCDValRef val); + +/** + * Builds a new map value. The 'maxcount' argument specifies how + * many entry slots to preallocate. Not more than that many + * entries may be inserted to the map using {@link NCDVal_MapInsert}. + * Returns a reference to the new value, or an invalid reference + * on out of memory. + */ +NCDValRef NCDVal_NewMap (NCDValMem *mem, size_t maxcount); + +/** + * Inserts an entry to the map value. + * The 'map' reference must point to a map value, and the + * 'key' and 'val' references must be non-invalid and point to values within + * the same memory object as the map. + * Inserting an entry does not in any way change the 'key'and 'val'; + * internally, the map only points to it. + * You must not modify the key after inserting it into a map. This is because + * the map builds an embedded AVL tree of entries indexed by keys. + * If insertion fails due to a maximum depth limit, returns 0. + * Otherwise returns 1, and *out_inserted is set to 1 if the key did not + * yet exist and the entry was inserted, and to 0 if it did exist and the + * entry was not inserted. The 'out_inserted' pointer may be NULL, in which + * case *out_inserted is never set. + */ +int NCDVal_MapInsert (NCDValRef map, NCDValRef key, NCDValRef val, int *out_inserted) WARN_UNUSED; + +/** + * Returns the number of entries in a map value, i.e. the number + * of times {@link NCDVal_MapInsert} was called successfully. + * The 'map' reference must point to a map value. + */ +size_t NCDVal_MapCount (NCDValRef map); + +/** + * Returns the maximum number of entries a map value may contain, + * i.e. the 'maxcount' argument to {@link NCDVal_NewMap}. + * The 'map' reference must point to a map value. + */ +size_t NCDVal_MapMaxCount (NCDValRef map); + +/** + * Determines if a map entry reference is invalid. This is used in combination + * with the map iteration functions to detect the end of iteration. + */ +int NCDVal_MapElemInvalid (NCDValMapElem me); + +/** + * Returns a reference to the first entry in a map, with respect to some + * arbitrary order. + * If the map is empty, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapFirst (NCDValRef map); + +/** + * Returns a reference to the entry in a map that follows the entry referenced + * by 'me', with respect to some arbitrary order. + * The 'me' argument must be a non-invalid reference to an entry in the map. + * If 'me' is the last entry, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapNext (NCDValRef map, NCDValMapElem me); + +/** + * Like {@link NCDVal_MapFirst}, but with respect to the order defined by + * {@link NCDVal_Compare}. + * Ordered iteration is slower and should only be used when needed. + */ +NCDValMapElem NCDVal_MapOrderedFirst (NCDValRef map); + +/** + * Like {@link NCDVal_MapNext}, but with respect to the order defined by + * {@link NCDVal_Compare}. + * Ordered iteration is slower and should only be used when needed. + */ +NCDValMapElem NCDVal_MapOrderedNext (NCDValRef map, NCDValMapElem me); + +/** + * Returns a reference to the key of the map entry referenced by 'me'. + * The 'me' argument must be a non-invalid reference to an entry in the map. + */ +NCDValRef NCDVal_MapElemKey (NCDValRef map, NCDValMapElem me); + +/** + * Returns a reference to the value of the map entry referenced by 'me'. + * The 'me' argument must be a non-invalid reference to an entry in the map. + */ +NCDValRef NCDVal_MapElemVal (NCDValRef map, NCDValMapElem me); + +/** + * Looks for a key in the map. The 'key' reference must be a non-invalid + * value reference, and may point to a value in a different memory object + * than the map. + * If the key exists in the map, returns a reference to the corresponding + * map entry. + * If the key does not exist, returns an invalid map entry reference. + */ +NCDValMapElem NCDVal_MapFindKey (NCDValRef map, NCDValRef key); + +/** + * Retrieves the value reference to the value of the map entry whose key is a + * string value equal to the given null-terminated string. If there is no such + * entry, returns an invalid value reference. + */ +NCDValRef NCDVal_MapGetValue (NCDValRef map, const char *key_str); + +/** + * Builds a placeholder replacement program, which is a list of instructions for + * efficiently replacing placeholders in identical values in identical memory + * objects. + * To actually perform replacements, make copies of the memory object of this value + * using {@link NCDValMem_InitCopy}, then call {@link NCDValReplaceProg_Execute} + * on the copies. + * The value passed must be a valid value, and not a placeholder. + * Returns 1 on success, 0 on failure. + */ +int NCDValReplaceProg_Init (NCDValReplaceProg *o, NCDValRef val); + +/** + * Frees the placeholder replacement program. + */ +void NCDValReplaceProg_Free (NCDValReplaceProg *o); + +/** + * Callback used by {@link NCDValReplaceProg_Execute} to allow the caller to produce + * values of placeholders. + * This function should build a new value within the memory object 'mem' (which is + * the same as of the memory object where placeholders are being replaced). + * On success, it should return 1, writing a valid value reference to *out. + * On failure, it can either return 0, or return 1 but write an invalid value reference. + * This callback must not access the memory object in any other way than building + * new values in it; it must not modify any values that were already present at the + * point it was called. + */ +typedef int (*NCDVal_replace_func) (void *arg, int plid, NCDValMem *mem, NCDValRef *out); + +/** + * Executes the replacement program, replacing placeholders in a value. + * The memory object must given be identical to the memory object which was used in + * {@link NCDValReplaceProg_Init}; see {@link NCDValMem_InitCopy}. + * This will call the callback 'replace', which should build the values to replace + * the placeholders. + * Returns 1 on success and 0 on failure. On failure, the entire memory object enters + * and inconsistent state and must be freed using {@link NCDValMem_Free} before + * performing any other operation on it. + * The program is passed by value instead of pointer because this appears to be faster. + * Is is not modified in any way. + */ +int NCDValReplaceProg_Execute (NCDValReplaceProg prog, NCDValMem *mem, NCDVal_replace_func replace, void *arg); + +#endif diff --git a/external/badvpn_dns/ncd/NCDValCons.c b/external/badvpn_dns/ncd/NCDValCons.c new file mode 100644 index 00000000..9872a478 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValCons.c @@ -0,0 +1,283 @@ +/** + * @file NCDValCons.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "NCDValCons.h" + +#define GROWARRAY_NAME NCDValCons__Array +#define GROWARRAY_OBJECT_TYPE NCDValCons +#define GROWARRAY_ARRAY_MEMBER elems +#define GROWARRAY_CAPACITY_MEMBER elems_capacity +#define GROWARRAY_MAX_CAPACITY INT_MAX +#include + +static int alloc_elem (NCDValCons *o) +{ + ASSERT(o->elems_size >= 0) + ASSERT(o->elems_size <= o->elems_capacity) + + if (o->elems_size == o->elems_capacity && !NCDValCons__Array_DoubleUp(o)) { + return -1; + } + + return o->elems_size++; +} + +static void assert_cons_val (NCDValCons *o, NCDValConsVal val) +{ +#ifndef NDEBUG + switch (val.cons_type) { + case NCDVALCONS_TYPE_COMPLETE: { + ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(o->mem, val.u.complete_ref))) + } break; + case NCDVALCONS_TYPE_INCOMPLETE_LIST: + case NCDVALCONS_TYPE_INCOMPLETE_MAP: { + ASSERT(val.u.incomplete.elems_idx >= -1) + ASSERT(val.u.incomplete.elems_idx < o->elems_size) + ASSERT(val.u.incomplete.count >= 0) + } break; + default: + ASSERT(0); + } +#endif +} + +static int complete_value (NCDValCons *o, NCDValConsVal val, NCDValSafeRef *out, int *out_error) +{ + assert_cons_val(o, val); + ASSERT(out) + ASSERT(out_error) + + switch (val.cons_type) { + case NCDVALCONS_TYPE_COMPLETE: { + *out = val.u.complete_ref; + } break; + + case NCDVALCONS_TYPE_INCOMPLETE_LIST: { + NCDValRef list = NCDVal_NewList(o->mem, val.u.incomplete.count); + if (NCDVal_IsInvalid(list)) { + goto fail_memory; + } + + int elemidx = val.u.incomplete.elems_idx; + + while (elemidx != -1) { + ASSERT(elemidx >= 0) + ASSERT(elemidx < o->elems_size) + + NCDValRef elem = NCDVal_FromSafe(o->mem, o->elems[elemidx].ref); + + if (!NCDVal_ListAppend(list, elem)) { + *out_error = NCDVALCONS_ERROR_DEPTH; + return 0; + } + + elemidx = o->elems[elemidx].next; + } + + *out = NCDVal_ToSafe(list); + } break; + + case NCDVALCONS_TYPE_INCOMPLETE_MAP: { + NCDValRef map = NCDVal_NewMap(o->mem, val.u.incomplete.count); + if (NCDVal_IsInvalid(map)) { + goto fail_memory; + } + + int keyidx = val.u.incomplete.elems_idx; + + while (keyidx != -1) { + ASSERT(keyidx >= 0) + ASSERT(keyidx < o->elems_size) + + int validx = o->elems[keyidx].next; + ASSERT(validx >= 0) + ASSERT(validx < o->elems_size) + + NCDValRef key = NCDVal_FromSafe(o->mem, o->elems[keyidx].ref); + NCDValRef value = NCDVal_FromSafe(o->mem, o->elems[validx].ref); + + int inserted; + if (!NCDVal_MapInsert(map, key, value, &inserted)) { + *out_error = NCDVALCONS_ERROR_DEPTH; + return 0; + } + if (!inserted) { + *out_error = NCDVALCONS_ERROR_DUPLICATE_KEY; + return 0; + } + + keyidx = o->elems[validx].next; + } + + *out = NCDVal_ToSafe(map); + } break; + + default: + ASSERT(0); + } + + return 1; + +fail_memory: + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; +} + +int NCDValCons_Init (NCDValCons *o, NCDValMem *mem) +{ + ASSERT(mem) + + o->mem = mem; + o->elems_size = 0; + + if (!NCDValCons__Array_Init(o, 1)) { + return 0; + } + + return 1; +} + +void NCDValCons_Free (NCDValCons *o) +{ + NCDValCons__Array_Free(o); +} + +int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error) +{ + ASSERT(out) + ASSERT(out_error) + + NCDValRef ref = NCDVal_NewStringBin(o->mem, data, len); + if (NCDVal_IsInvalid(ref)) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + out->cons_type = NCDVALCONS_TYPE_COMPLETE; + out->u.complete_ref = NCDVal_ToSafe(ref); + + return 1; +} + +void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out) +{ + ASSERT(out) + + out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_LIST; + out->u.incomplete.elems_idx = -1; + out->u.incomplete.count = 0; +} + +void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out) +{ + ASSERT(out) + + out->cons_type = NCDVALCONS_TYPE_INCOMPLETE_MAP; + out->u.incomplete.elems_idx = -1; + out->u.incomplete.count = 0; +} + +int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error) +{ + assert_cons_val(o, *list); + ASSERT(list->cons_type == NCDVALCONS_TYPE_INCOMPLETE_LIST) + assert_cons_val(o, elem); + ASSERT(out_error) + + int elemidx = alloc_elem(o); + if (elemidx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + o->elems[elemidx].next = list->u.incomplete.elems_idx; + + if (!complete_value(o, elem, &o->elems[elemidx].ref, out_error)) { + return 0; + } + + list->u.incomplete.elems_idx = elemidx; + list->u.incomplete.count++; + + return 1; +} + +int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error) +{ + assert_cons_val(o, *map); + ASSERT(map->cons_type == NCDVALCONS_TYPE_INCOMPLETE_MAP) + assert_cons_val(o, key); + assert_cons_val(o, value); + ASSERT(out_error) + + int validx = alloc_elem(o); + if (validx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + int keyidx = alloc_elem(o); + if (keyidx < 0) { + *out_error = NCDVALCONS_ERROR_MEMORY; + return 0; + } + + o->elems[validx].next = map->u.incomplete.elems_idx; + o->elems[keyidx].next = validx; + + if (!complete_value(o, value, &o->elems[validx].ref, out_error)) { + return 0; + } + + if (!complete_value(o, key, &o->elems[keyidx].ref, out_error)) { + return 0; + } + + map->u.incomplete.elems_idx = keyidx; + map->u.incomplete.count++; + + return 1; +} + +int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error) +{ + assert_cons_val(o, val); + ASSERT(out) + ASSERT(out_error) + + NCDValSafeRef sref; + if (!complete_value(o, val, &sref, out_error)) { + return 0; + } + + *out = NCDVal_FromSafe(o->mem, sref); + return 1; +} diff --git a/external/badvpn_dns/ncd/NCDValCons.h b/external/badvpn_dns/ncd/NCDValCons.h new file mode 100644 index 00000000..3b25d666 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValCons.h @@ -0,0 +1,176 @@ +/** + * @file NCDValCons.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALCONS_H +#define BADVPN_NCDVALCONS_H + +#include +#include + +#include +#include + +struct NCDValCons__temp_elem { + NCDValSafeRef ref; + int next; +}; + +/** + * Value constructor; implements a mechanism for efficiently constructing + * NCD values into {@link NCDVal} compact representation, but without + * having to know the number of list or map elements in advance. + * For the purpose of value construction, values are representing using + * {@link NCDValConsVal} objects. + */ +typedef struct { + NCDValMem *mem; + struct NCDValCons__temp_elem *elems; + int elems_size; + int elems_capacity; +} NCDValCons; + +#define NCDVALCONS_TYPE_COMPLETE 1 +#define NCDVALCONS_TYPE_INCOMPLETE_LIST 2 +#define NCDVALCONS_TYPE_INCOMPLETE_MAP 3 + +/** + * Abstract handle which represents a value during constuction via + * {@link NCDValCons}. + */ +typedef struct { + int cons_type; + union { + NCDValSafeRef complete_ref; + struct { + int elems_idx; + int count; + } incomplete; + } u; +} NCDValConsVal; + +#define NCDVALCONS_ERROR_MEMORY 1 +#define NCDVALCONS_ERROR_DUPLICATE_KEY 2 +#define NCDVALCONS_ERROR_DEPTH 3 + +/** + * Initializes a value constructor. + * + * @param o value constructor to initialize + * @param mem memory object where values will be stored into + * @return 1 on success, 0 on failure + */ +int NCDValCons_Init (NCDValCons *o, NCDValMem *mem) WARN_UNUSED; + +/** + * Frees the value constructor. This only means the constuctor does + * not exist any more; any values constructed and completed using + * {@link NCDValCons_Complete} remain in the memory object. + * + * @param o value constructor to free + */ +void NCDValCons_Free (NCDValCons *o); + +/** + * Creates a new string value with the given data. + * + * @param o value constructor + * @param data pointer to string data. This must not point into the + * memory object the value constructor is using. The data + * is copied. + * @param len length of the string + * @param out on success, *out will be set to a handle representing + * the new string + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_NewString (NCDValCons *o, const uint8_t *data, size_t len, NCDValConsVal *out, int *out_error) WARN_UNUSED; + +/** + * Creates an empty list value. + * + * @param o value constructor + * @param out *out will be set to a handle representing the new list + */ +void NCDValCons_NewList (NCDValCons *o, NCDValConsVal *out); + +/** + * Creates an empty map value. + * + * @param o value constructor + * @param out *out will be set to a handle representing the new map + */ +void NCDValCons_NewMap (NCDValCons *o, NCDValConsVal *out); + +/** + * Prepends an element to a list value. + * + * @param o value constructor + * @param list pointer to the handle representing the list. On success, + * the handle will be modified, and the old handle must not + * be used any more. + * @param elem handle representing the value to be prepended. This handle + * must not be used any more after being prepended to the list. + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_ListPrepend (NCDValCons *o, NCDValConsVal *list, NCDValConsVal elem, int *out_error) WARN_UNUSED; + +/** + * Inserts an entry into a map value. + * + * @param o value constructor + * @param map pointer to the handle representing the map. On success, + * the handle will be modified, and the old handle must not + * be used any more. + * @param key handle representing the key of the entry. This handle + * must not be used any more after being inserted into the map. + * @param value handle representing the value of the entry. This handle + * must not be used any more after being inserted into the + * map. + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_MapInsert (NCDValCons *o, NCDValConsVal *map, NCDValConsVal key, NCDValConsVal value, int *out_error) WARN_UNUSED; + +/** + * Completes a value represented by a {@link NCDValConsVal} handle, + * producing a {@link NCDValRef} object which refers to this value within + * the memory object. + * + * @param o value constructor + * @param val handle representing the value to be completed. After a value + * is completed, the handle must not be used any more. + * @param out on success, *out will be set to a {@link NCDValRef} object + * referencing the completed value + * @param out_error on failure, *out_error will be set to an error code + * @return 1 on success, 0 on failure + */ +int NCDValCons_Complete (NCDValCons *o, NCDValConsVal val, NCDValRef *out, int *out_error) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDValGenerator.c b/external/badvpn_dns/ncd/NCDValGenerator.c new file mode 100644 index 00000000..3355f8b9 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValGenerator.c @@ -0,0 +1,193 @@ +/** + * @file NCDValGenerator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDValGenerator.h" + +#include + +static int generate_val (NCDValRef value, ExpString *out_str) +{ + ASSERT(!NCDVal_IsInvalid(value)) + + switch (NCDVal_Type(value)) { + case NCDVAL_STRING: { + b_cstring cstr = NCDVal_StringCstring(value); + + if (!ExpString_AppendChar(out_str, '"')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + B_CSTRING_LOOP_CHARS(cstr, char_pos, ch, { + if (ch == '\0') { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02"PRIx8, (uint8_t)ch); + + if (!ExpString_Append(out_str, buf)) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + + goto next_char; + } + + if (ch == '"' || ch == '\\') { + if (!ExpString_AppendChar(out_str, '\\')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, ch)) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + next_char:; + }) + + if (!ExpString_AppendChar(out_str, '"')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + case NCDVAL_LIST: { + size_t count = NCDVal_ListCount(value); + + if (!ExpString_AppendChar(out_str, '{')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + for (size_t i = 0; i < count; i++) { + if (i > 0) { + if (!ExpString_Append(out_str, ", ")) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + } + + if (!generate_val(NCDVal_ListGet(value, i), out_str)) { + goto fail; + } + } + + if (!ExpString_AppendChar(out_str, '}')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + case NCDVAL_MAP: { + if (!ExpString_AppendChar(out_str, '[')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + int is_first = 1; + + for (NCDValMapElem e = NCDVal_MapOrderedFirst(value); !NCDVal_MapElemInvalid(e); e = NCDVal_MapOrderedNext(value, e)) { + NCDValRef ekey = NCDVal_MapElemKey(value, e); + NCDValRef eval = NCDVal_MapElemVal(value, e); + + if (!is_first) { + if (!ExpString_Append(out_str, ", ")) { + BLog(BLOG_ERROR, "ExpString_Append failed"); + goto fail; + } + } + + if (!generate_val(ekey, out_str)) { + goto fail; + } + + if (!ExpString_AppendChar(out_str, ':')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + + if (!generate_val(eval, out_str)) { + goto fail; + } + + is_first = 0; + } + + if (!ExpString_AppendChar(out_str, ']')) { + BLog(BLOG_ERROR, "ExpString_AppendChar failed"); + goto fail; + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +char * NCDValGenerator_Generate (NCDValRef value) +{ + ASSERT(!NCDVal_IsInvalid(value)) + + ExpString str; + if (!ExpString_Init(&str)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + if (!generate_val(value, &str)) { + goto fail1; + } + + return ExpString_Get(&str); + +fail1: + ExpString_Free(&str); +fail0: + return NULL; +} + +int NCDValGenerator_AppendGenerate (NCDValRef value, ExpString *str) +{ + ASSERT(!NCDVal_IsInvalid(value)) + ASSERT(str) + + return generate_val(value, str); +} diff --git a/external/badvpn_dns/ncd/NCDValGenerator.h b/external/badvpn_dns/ncd/NCDValGenerator.h new file mode 100644 index 00000000..35fd9917 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValGenerator.h @@ -0,0 +1,40 @@ +/** + * @file NCDValGenerator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALUEGENERATOR_H +#define BADVPN_NCDVALUEGENERATOR_H + +#include +#include +#include + +char * NCDValGenerator_Generate (NCDValRef value); +int NCDValGenerator_AppendGenerate (NCDValRef value, ExpString *str) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDValParser.c b/external/badvpn_dns/ncd/NCDValParser.c new file mode 100644 index 00000000..7cdb4dac --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValParser.c @@ -0,0 +1,225 @@ +/** + * @file NCDValParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NCDValParser.h" + +#include + +struct token { + char *str; + size_t len; +}; + +struct value { + int have; + NCDValConsVal v; +}; + +#define ERROR_FLAG_MEMORY (1 << 0) +#define ERROR_FLAG_TOKENIZATION (1 << 1) +#define ERROR_FLAG_SYNTAX (1 << 2) +#define ERROR_FLAG_DUPLICATE_KEY (1 << 3) +#define ERROR_FLAG_DEPTH (1 << 4) + +struct parser_state { + NCDValCons cons; + NCDValRef value; + int cons_error; + int error_flags; + void *parser; +}; + +static void free_token (struct token o) +{ + if (o.str) { + free(o.str); + } +}; + +static void handle_cons_error (struct parser_state *state) +{ + switch (state->cons_error) { + case NCDVALCONS_ERROR_MEMORY: + state->error_flags |= ERROR_FLAG_MEMORY; + break; + case NCDVALCONS_ERROR_DUPLICATE_KEY: + state->error_flags |= ERROR_FLAG_DUPLICATE_KEY; + break; + case NCDVALCONS_ERROR_DEPTH: + state->error_flags |= ERROR_FLAG_DEPTH; + break; + default: + ASSERT(0); + } +} + +// rename non-static functions defined by our Lemon parser +// to avoid clashes with other Lemon parsers +#define ParseTrace ParseTrace_NCDValParser +#define ParseAlloc ParseAlloc_NCDValParser +#define ParseFree ParseFree_NCDValParser +#define Parse Parse_NCDValParser + +// include the generated Lemon parser +#include "../generated/NCDValParser_parse.c" +#include "../generated/NCDValParser_parse.h" + +static int tokenizer_output (void *user, int token, char *value, size_t value_len, size_t line, size_t line_char) +{ + struct parser_state *state = user; + ASSERT(!state->error_flags) + + if (token == NCD_ERROR) { + state->error_flags |= ERROR_FLAG_TOKENIZATION; + goto fail; + } + + struct token minor; + minor.str = value; + minor.len = value_len; + + switch (token) { + case NCD_EOF: { + Parse(state->parser, 0, minor, state); + } break; + + case NCD_TOKEN_CURLY_OPEN: { + Parse(state->parser, CURLY_OPEN, minor, state); + } break; + + case NCD_TOKEN_CURLY_CLOSE: { + Parse(state->parser, CURLY_CLOSE, minor, state); + } break; + + case NCD_TOKEN_COMMA: { + Parse(state->parser, COMMA, minor, state); + } break; + + case NCD_TOKEN_STRING: { + Parse(state->parser, STRING, minor, state); + } break; + + case NCD_TOKEN_COLON: { + Parse(state->parser, COLON, minor, state); + } break; + + case NCD_TOKEN_BRACKET_OPEN: { + Parse(state->parser, BRACKET_OPEN, minor, state); + } break; + + case NCD_TOKEN_BRACKET_CLOSE: { + Parse(state->parser, BRACKET_CLOSE, minor, state); + } break; + + default: + state->error_flags |= ERROR_FLAG_TOKENIZATION; + free_token(minor); + goto fail; + } + + if (state->error_flags) { + goto fail; + } + + return 1; + +fail: + ASSERT(state->error_flags) + + if ((state->error_flags & ERROR_FLAG_MEMORY)) { + BLog(BLOG_ERROR, "line %zu, character %zu: memory allocation error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_TOKENIZATION)) { + BLog(BLOG_ERROR, "line %zu, character %zu: tokenization error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_SYNTAX)) { + BLog(BLOG_ERROR, "line %zu, character %zu: syntax error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_DUPLICATE_KEY)) { + BLog(BLOG_ERROR, "line %zu, character %zu: duplicate key in map error", line, line_char); + } + + if ((state->error_flags & ERROR_FLAG_DEPTH)) { + BLog(BLOG_ERROR, "line %zu, character %zu: depth limit exceeded", line, line_char); + } + + return 0; +} + +int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(str_len == 0 || str) + ASSERT(mem) + ASSERT(out_value) + + int ret = 0; + + struct parser_state state; + state.value = NCDVal_NewInvalid(); + state.error_flags = 0; + + if (!NCDValCons_Init(&state.cons, mem)) { + BLog(BLOG_ERROR, "NCDValCons_Init failed"); + goto fail0; + } + + if (!(state.parser = ParseAlloc(malloc))) { + BLog(BLOG_ERROR, "ParseAlloc failed"); + goto fail1; + } + + NCDConfigTokenizer_Tokenize((char *)str, str_len, tokenizer_output, &state); + + ParseFree(state.parser, free); + + if (state.error_flags) { + goto fail1; + } + + ASSERT(!NCDVal_IsInvalid(state.value)) + + *out_value = state.value; + ret = 1; + +fail1: + NCDValCons_Free(&state.cons); +fail0: + return ret; +} diff --git a/external/badvpn_dns/ncd/NCDValParser.h b/external/badvpn_dns/ncd/NCDValParser.h new file mode 100644 index 00000000..57d37b0e --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValParser.h @@ -0,0 +1,50 @@ +/** + * @file NCDValParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDVALUEPARSER_H +#define BADVPN_NCDVALUEPARSER_H + +#include + +#include +#include + +/** + * Parses an NCD value string into {@link NCDVal} compact representation. + * + * @param str pointer to the string to be parsed + * @param str_len length of the string in bytes + * @param mem value memory object which the result will be stored in + * @param out_value on success, the value reference of the result will be + * written here + * @return 1 on success, 0 on failure + */ +int NCDValParser_Parse (const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out_value) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/ncd/NCDValParser_parse.y b/external/badvpn_dns/ncd/NCDValParser_parse.y new file mode 100644 index 00000000..ced123d3 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDValParser_parse.y @@ -0,0 +1,202 @@ +/** + * @file NCDConfigParser_parse.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// argument for passing state to parser hooks +%extra_argument { struct parser_state *parser_out } + +// type of structure representing tokens +%token_type { struct token } + +// token destructor frees extra memory allocated for tokens +%token_destructor { free_token($$); } + +// types of nonterminals +%type list_contents { struct value } +%type list { struct value } +%type map_contents { struct value } +%type map { struct value } +%type value { struct value } + +// mention parser_out in some destructor to and avoid unused variable warning +%destructor list_contents { (void)parser_out; } + +// try to dynamically grow the parse stack +%stack_size 0 + +// on syntax error, set the corresponding error flag +%syntax_error { + parser_out->error_flags |= ERROR_FLAG_SYNTAX; +} + +// workaroud Lemon bug: if the stack overflows, the token that caused the overflow will be leaked +%stack_overflow { + if (yypMinor) { + free_token(yypMinor->yy0); + } +} + +input ::= value(A). { + if (!A.have) { + goto failZ0; + } + + if (!NCDVal_IsInvalid(parser_out->value)) { + // should never happen + parser_out->error_flags |= ERROR_FLAG_SYNTAX; + goto failZ0; + } + + if (!NCDValCons_Complete(&parser_out->cons, A.v, &parser_out->value, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failZ0; + } + +failZ0:; +} + +list_contents(R) ::= value(A). { + if (!A.have) { + goto failL0; + } + + NCDValCons_NewList(&parser_out->cons, &R.v); + + if (!NCDValCons_ListPrepend(&parser_out->cons, &R.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failL0; + } + + R.have = 1; + goto doneL; + +failL0: + R.have = 0; +doneL:; +} + +list_contents(R) ::= value(A) COMMA list_contents(N). { + if (!A.have || !N.have) { + goto failM0; + } + + if (!NCDValCons_ListPrepend(&parser_out->cons, &N.v, A.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failM0; + } + + R.have = 1; + R.v = N.v; + goto doneM; + +failM0: + R.have = 0; +doneM:; +} + +list(R) ::= CURLY_OPEN CURLY_CLOSE. { + NCDValCons_NewList(&parser_out->cons, &R.v); + R.have = 1; +} + +list(R) ::= CURLY_OPEN list_contents(A) CURLY_CLOSE. { + R = A; +} + +map_contents(R) ::= value(A) COLON value(B). { + if (!A.have || !B.have) { + goto failS0; + } + + NCDValCons_NewMap(&parser_out->cons, &R.v); + + if (!NCDValCons_MapInsert(&parser_out->cons, &R.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failS0; + } + + R.have = 1; + goto doneS; + +failS0: + R.have = 0; +doneS:; +} + +map_contents(R) ::= value(A) COLON value(B) COMMA map_contents(N). { + if (!A.have || !B.have || !N.have) { + goto failT0; + } + + if (!NCDValCons_MapInsert(&parser_out->cons, &N.v, A.v, B.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failT0; + } + + R.have = 1; + R.v = N.v; + goto doneT; + +failT0: + R.have = 0; +doneT:; +} + +map(R) ::= BRACKET_OPEN BRACKET_CLOSE. { + NCDValCons_NewMap(&parser_out->cons, &R.v); + R.have = 1; +} + +map(R) ::= BRACKET_OPEN map_contents(A) BRACKET_CLOSE. { + R = A; +} + +value(R) ::= STRING(A). { + ASSERT(A.str) + + if (!NCDValCons_NewString(&parser_out->cons, (const uint8_t *)A.str, A.len, &R.v, &parser_out->cons_error)) { + handle_cons_error(parser_out); + goto failU0; + } + + R.have = 1; + goto doneU; + +failU0: + R.have = 0; +doneU:; + free_token(A); +} + +value(R) ::= list(A). { + R = A; +} + +value(R) ::= map(A). { + R = A; +} diff --git a/external/badvpn_dns/ncd/NCDVal_maptree.h b/external/badvpn_dns/ncd/NCDVal_maptree.h new file mode 100644 index 00000000..d5b9c0c3 --- /dev/null +++ b/external/badvpn_dns/ncd/NCDVal_maptree.h @@ -0,0 +1,15 @@ +#define CAVL_PARAM_NAME NCDVal__MapTree +#define CAVL_PARAM_FEATURE_COUNTS 0 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS 0 +#define CAVL_PARAM_TYPE_ENTRY NCDVal__maptree_entry +#define CAVL_PARAM_TYPE_LINK NCDVal__idx +#define CAVL_PARAM_TYPE_KEY NCDValRef +#define CAVL_PARAM_TYPE_ARG NCDVal__maptree_arg +#define CAVL_PARAM_VALUE_NULL ((NCDVal__idx)-1) +#define CAVL_PARAM_FUN_DEREF(arg, link) ((struct NCDVal__mapelem *)NCDValMem__BufAt((arg), (link))) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, node1, node2) NCDVal_Compare(NCDVal__Ref((arg), (node1).ptr->key_idx), NCDVal__Ref((arg), (node2).ptr->key_idx)) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, node2) NCDVal_Compare((key1), NCDVal__Ref((arg), (node2).ptr->key_idx)) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent diff --git a/external/badvpn_dns/ncd/README b/external/badvpn_dns/ncd/README new file mode 100644 index 00000000..05d638f2 --- /dev/null +++ b/external/badvpn_dns/ncd/README @@ -0,0 +1,386 @@ +# This file contains some examples of using NCD, the Network Configuration Daemon. +# +# A short introduction to NCD follows. +# +# NCD is a general-purpose system configuration system, operated with a unique programming language. +# The configuration consists of one or more so-called processes that can be considered executing in +# parallel. Further, each process consists of one or more statements, representing the individual +# actions. Statements are implemented as modules built into NCD. +# +# Inside a process, statements can be considered "executed" one after another. That is, when NCD +# starts up, it initializes the first statement, putting it in the DOWN state. When the statement +# reports having transitioned into the UP state, it initializes the next statement in the DOWN state, +# and so on. +# +# However, execution can go in the other direction too. A statement in the UP state can, at any time, +# report having transitioned into the DOWN state. At this point, any statements after that one will +# automatically be de-initialized. The de-initiazation is done from the bottom up. First the last +# initialized statement after the problematic statement is requested to terminate and enters the +# DYING state. After it terminates, its preceding statement enters the DYING state, and so on, until +# all statements following the problematic statement have been de-initiazed. +# +# The backward-execution is the key feature of NCD, and is particularly well suited for programming +# system configurations. Read on to see why. +# +# Statements in NCD can be divided into two categories: +# - Statements that configure something. These statements transition into the UP state "immediately". +# On de-initialization, such statements perform the reverse operation of what they did when initialized. +# Imaginary example: a statement that turn a light on intialization, and turns if off on de-initialization. +# - Statements that wait for something. These statements may remain in the DOWN state indefinitely. +# They enter the UP state when the waited-for condition is satisfied, and also go back into the DOWN +# state when it is no longer satisfied. +# Imaginary example: a statement that is UP when a switch is turned on, and DOWN when it is turned off. +# +# Using the two example statements, we can constuct a process that controls the light based on the switch: +# (these are not really implemented in NCD :) +# +# process light { +# wait_switch(); +# turn_light(); +# } +# +# When the switch is turned on, wait_switch() will transition to UP, initializing turn_light(), turning the +# light on. When the switch is turned off, wait_switch() will transition to DOWN, causing the de-initialization +# of turn_light(), turning the light off. +# We can add another turn_light() at the end to make the switch control two lights. +# +# A more complex example: We have a christmas three with lights on it. There are multiple "regular" lights, +# controlled with switches, and a special "top" light. The regular lights take a long time to turn on, and +# each takes a different, unpredictable time. We want the top light to be turned on if and only if all the regular +# lights are completely on. +# +# This problem can easily be solved using dependencies. NCD has built-in support for dependencies, provided +# in the form of provide() and depend() statements. A depend() statement is DOWN when its corresponding +# provide() statement is not initialized, and UP when it is. When a provide() is requested to de-initialize, it +# transitions the depend() statements back into the DOWN state, and, before actually dying, waits for any +# statements following them to de-initialize. +# +# The christmas three problem can then be programmed as follows: +# +# process light1 { +# wait_switch1(); +# turn_light1(); +# provide("L1"); +# } +# +# process light2 { +# wait_switch2(); +# turn_light2(); +# provide("L2"); +# } +# +# process top_light { +# depend("L1"); +# depend("L2"); +# turn_top_light(); +# } +# +# Follow some real examples of network configuration using NCD. +# For a list of implemented statements and their descriptions, take a look at the BadVPN source code, in +# the ncd/modules/ folder. +# + +# +# Network card using DHCP. +# + +process lan { + # Make the interface name a variable so we can refer to it. + # The NCD language has no notion of assigning a variable. Instead variables are + # provided by statements preceding the statement where they are used. + # The built-in var() statement can be used to make an alias. + var("eth0") dev; + + # Wait for the network card to appear, set it up and wait for the cable to be + # plugged it. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Start DHCP. + net.ipv4.dhcp(dev) dhcp; + + # DHCP has obtained an address. + # Because net.ipv4.dhcp does no checks of the IP address, as a safety measure, do not proceed + # if the address is local. + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + + # Assign the obtained address to the interface. + net.ipv4.addr(dev, dhcp.addr, dhcp.prefix); + + # Add a default route. + # + net.ipv4.route("0.0.0.0", "0", dhcp.gateway, "20", dev); + + # Add DNS servers, as provided by DHCP. + # "20" is the priority of the servers. When applying DNS servers, NCD collects the servers + # from all active net.dns() statements, sorts them by priority ascending (stable), and writes + # them to /etc/resolv.conf, overwriting anything that was previously there. + net.dns(dhcp.dns_servers, "20"); +} + +# +# Network card with static configuration. +# + +process lan2 { + # Make the interface name a variable so we can refer to it. + var("eth1") dev; + + # Wait for the network card to appear, set it up and wait for the cable to be + # plugged it. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Assign an IP address. + # "24" is prefix length, i.e. subnet mask 255.255.255.0 + net.ipv4.addr(dev, "192.168.62.3", "24"); + + # Add a default route. + net.ipv4.route("0.0.0.0", "0", "192.168.62.3", "20", dev); + + # Build a list of DNS servers. + # The NCD language does not support "expressions" - statement arguments must be + # constant strings or variables referring to preceding statements. + # A list can be constructed using the built-in list() statement. + list("192.168.62.5", "192.168.62.6") dns_servers; + + # Add the DNS servers. + net.dns(dns_servers, "20"); +} + +# +# Wireless network interface using wpa_supplicant. +# + +process WLAN { + # Set device. + var("wlan0") dev; + + # Wait for device and rfkill switch. + net.backend.waitdevice(dev); + net.backend.rfkill("wlan", dev); + + # Start wpa_supplicant on this interface, using configuration in /etc/wpa_supplicant/all.conf . + list() args; + net.backend.wpa_supplicant(dev, "/etc/wpa_supplicant/all.conf", "/usr/sbin/wpa_supplicant", args) sup; + + # wpa_supplicant tells us what network we connected to. Look below for how this can be used to + # have different configurations, "BadVPN, but configured differently based on what network we're in". + println("connected to wireless network: bssid=", sup.bssid, " ssid=", sup.ssid); + + # Wireless connection successful, here comes network config (DHCP/static/whatever) ... +} + +# +# A BadVPN VPN interface for access to the virtual network (only). +# + +process lan { + ... (something like above) ... + + # Alias our IP address for easy access from the "vpn" process (or, for a static address, alias + # it before assigning it, and assign it using the alias). + var(dhcp.addr) ipaddr; + + # Allow VPN to start at this point. + # (and require it to stop before deconfiguring the interface if e.g. the cable is plugged out) + provide("LAN"); +} + +process vpn { + # Need the local interface to be working in order start VPN. + depend("LAN") landep; + + # Choose the name of the network interface. + var("tap3") dev; + + # Construct command line arguments for badvpn-client. Adapt according to your setup. + # "--tapdev" will be provided automatically. + + # Alias the port number that the VPN process will bind to. + var("6000") port; + + # Construct dynamic parts of command line options. + # The VPN client program needs to know some IP addresses in order to tell other peers where to connect to. + # Obtain this informations from variables in the "lan" process through the depend() statement. + + # Construct the local address (addr + port). + concat(landep.ipaddr, ":", port) local_addr_arg; + + # Construct the Internet address (assuming we are behind a NAT). + # Need to know the NAT's external address here. But we could queried it somehow. + # That is if we have preconfigured the NAT router to forward ports. But we could implement a statement + # that obtains the mappings dynamically with UPnP! + concat("1.2.3.4", ":", port) internet_addr_arg; + + # Finally construct the complete arguments, using the above address arguments. + list( + "--logger", "syslog", "--syslog-ident", "badvpn", + "--server-addr", "badvpn.example.com:7000", + "--ssl", "--nssdb", "sql:/home/badvpn/nssdb", "--client-cert-name", "peer-someone", + "--transport-mode", "udp", "--encryption-mode", "blowfish", "--hash-mode", "md5", "--otp", "blowfish", "3000", "2000", + "--scope", "mylan", "--scope", "internet", + "--bind-addr", "0.0.0.0:6000", "--num-ports", "20", + "--ext-addr", local_addr_arg, "mylan", + "--ext-addr", internet_addr_arg, "internet" + ) args; + + # Start the BadVPN backend. + # "badvpn" is the user account which the VPN client will run as. + # If you use SSL, the NSS database must be accessible to this user. + net.backend.badvpn(dev, "badvpn", "/usr/bin/badvpn-client-26", args); + + # Assign an IP address to the VPN interface. + # (we could easily use DHCP here!) + net.ipv4.addr(dev, "10.0.0.1", "24"); +} + +# +# BadVPN, but configured differently based on what network we're in. +# The network is identified based on the IP address we were assigned by DHCP. +# The different configuration provide specific arguents to badvpn-client. +# + +process lan { + ... (interface config stuff using DHCP, see above) ... + ... (the 'ipaddr' variable holds the local IP address) ... + + # Match the address to various known networks. + ip_in_network(ipaddr, "192.168.4.0", "24") is_lan1; + ip_in_network(ipaddr, "192.168.7.0", "24") is_lan2; + + # Allow VPN to start at this point. + provide("LAN"); +} + +process vpn { + ... + + # Construct common arguments here ... + list( ... ) common_args; + + # Choose appropriate configuration by waking up the configuration processes + # and waiting for one to complete. + provide("VPN_CONF_START"); + depend("VPN_CONF_END") config; + + # Concatenate common and configuration-specific arguments. + concatlist(common_args, config.args) args; + + ... +} + +process vpn_config_lan1 { + depend("VPN_CONF_START") dep; + + # Proceed only if we're in lan1. + if(dep.landep.is_lan1); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +process vpn_config_lan2 { + depend("VPN_CONF_START") dep; + + # Proceed only if we're in lan2. + if(dep.landep.is_lan2); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +process vpn_config_inet { + depend("VPN_CONF_START") dep; + + # Proceed only if we're not in any known network. + ifnot(dep.landep.is_lan1); + ifnot(dep.landep.is_lan2); + + list( + ... + ) args; + + provide("VPN_CONF_END"); +} + +# +# Two wired network interfaces (eth0, eth1), both of which may be used for Internet access. +# When both are working, give priority to eth1 (e.g. if eth0 is up, but later eth1 also comes +# up, the configuration will be changed to use eth1 for Internet access). +# + +process eth0 { + # Set device. + var("eth0") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + multiprovide("NET-eth0"); +} + +process eth1 { + # Set device. + var("eth1") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Static configuration. + var("192.168.111.116") addr; + var("24") addr_prefix; + var("192.168.111.1") gateway; + list("192.168.111.14", "193.2.1.66") dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + multiprovide("NET-eth1"); +} + +process NETCONF { + # Wait for some network connection. Prefer eth1 by putting it in front of eth0. + list("NET-eth1", "NET-eth0") pnames; + multidepend(pnames) ifdep; + + # Alias device values. + var(ifdep.dev) dev; + var(ifdep.addr) addr; + var(ifdep.addr_prefix) addr_prefix; + var(ifdep.gateway) gateway; + var(ifdep.dns_servers) dns_servers; + + # Add default route. + net.ipv4.route("0.0.0.0", "0", gateway, "20", dev); + + # Configure DNS servers. + net.dns(dns_servers, "20"); +} diff --git a/external/badvpn_dns/ncd/emncd.c b/external/badvpn_dns/ncd/emncd.c new file mode 100644 index 00000000..db41968e --- /dev/null +++ b/external/badvpn_dns/ncd/emncd.c @@ -0,0 +1,137 @@ +/** + * @file emncd.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static BReactor reactor; +static int running; +static NCDInterpreter interpreter; + +static void interpreter_handler_finished (void *user, int exit_code) +{ + ASSERT(running) + + fprintf(stderr, "--- interpreter terminated ---\n"); + + NCDInterpreter_Free(&interpreter); + + running = 0; +} + +__attribute__((used)) +int main () +{ + BLog_InitStderr(); + + fprintf(stderr, "--- initializing emncd version "GLOBAL_VERSION" ---\n"); + + BTime_Init(); + + BReactor_EmscriptenInit(&reactor); + + running = 0; + + return 0; +} + +__attribute__((used)) +void emncd_start (const char *program_text, int loglevel) +{ + ASSERT(program_text); + ASSERT(loglevel >= 0); + ASSERT(loglevel <= BLOG_DEBUG); + + if (running) { + fprintf(stderr, "--- cannot start, interpreter is already running! ---\n"); + return; + } + + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + BLog_SetChannelLoglevel(i, loglevel); + } + + // parse program + NCDProgram program; + if (!NCDConfigParser_Parse((char *)program_text ,strlen(program_text), &program)) { + fprintf(stderr, "--- error: failed to parse the program ---\n"); + return; + } + + // include commands are not implemented currently + if (NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE) || NCDProgram_ContainsElemType(&program, NCDPROGRAMELEM_INCLUDE_GUARD)) { + fprintf(stderr, "--- error: include mechanism is not supported in emncd ---\n"); + NCDProgram_Free(&program); + return; + } + + fprintf(stderr, "--- starting interpreter ---\n"); + + struct NCDInterpreter_params params; + params.handler_finished = interpreter_handler_finished; + params.user = NULL; + params.retry_time = 5000; + params.extra_args = NULL; + params.num_extra_args = 0; + params.reactor = &reactor; + + if (!NCDInterpreter_Init(&interpreter, program, params)) { + fprintf(stderr, "--- failed to initialize the interpreter ---\n"); + return; + } + + running = 1; + + BReactor_EmscriptenSync(&reactor); +} + +__attribute__((used)) +void emncd_stop (void) +{ + if (!running) { + fprintf(stderr, "--- cannot request termination, interpreter is not running! ---\n"); + return; + } + + fprintf(stderr, "--- requesting interpreter termination ---\n"); + + NCDInterpreter_RequestShutdown(&interpreter, 0); + + BReactor_EmscriptenSync(&reactor); +} diff --git a/external/badvpn_dns/ncd/emncd.html b/external/badvpn_dns/ncd/emncd.html new file mode 100644 index 00000000..befc0708 --- /dev/null +++ b/external/badvpn_dns/ncd/emncd.html @@ -0,0 +1,320 @@ + + + +NCD in Javascript + + + + + + + + +This is a quick port of my NCD programming language +to Javascript using the Emscripten compiler. +
+ +Please open the Javascript console so you can see the output (Chrome: CTRL+Shift+J. Firefox: CTRL+Shift+K). +
+ + + + + + + + + + + + + +Examples: + + + + + + +
+ + +
+ +Loglevel: + None + Error + Warning + Notice + Info + Debug
+ + + +
+Note: if you get the interpreter stuck and unable to terminate, just refresh the page + + + diff --git a/external/badvpn_dns/ncd/examples/dbus_start.ncd b/external/badvpn_dns/ncd/examples/dbus_start.ncd new file mode 100644 index 00000000..832b3595 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/dbus_start.ncd @@ -0,0 +1,82 @@ +process main { + call("dbus_start", {{"--session", "--nopidfile"}}) dbus_start; + + println("Got dbus: ", dbus_start.dbus_address); + rprintln("Lost dbus"); +} + +template dbus_start { + alias("_arg0") dbus_args; + + var("false") retrying; + + backtrack_point() retry_point; + If (retrying) { + println("dbus_start: retrying in one second"); + sleep("1000", "0"); + }; + + retrying->set("true"); + + listfrom({"/usr/bin/dbus-daemon"}, dbus_args, {"--print-address"}) dbus_command; + + sys.start_process(dbus_command, "r") proc; + If (proc.is_error) { + println("dbus_start: error starting process"); + retry_point->go(); + }; + + var("") dbus_address; + blocker() finished_blocker; + + process_manager() mgr; + mgr->start("dbus_start__waiter", {}); + mgr->start("dbus_start__reader", {}); + + finished_blocker->use(); +} + +template dbus_start__waiter { + alias("_caller.proc") proc; + alias("_caller.retry_point") retry_point; + + proc->wait(); + println("dbus_start: process terminated"); + retry_point->go(); +} + +template dbus_start__reader { + alias("_caller.proc") proc; + alias("_caller.retry_point") retry_point; + alias("_caller.dbus_address") dbus_address; + alias("_caller.finished_blocker") finished_blocker; + + proc->read_pipe() rpipe; + If (rpipe.is_error) { + println("dbus_start: read pipe error"); + retry_point->go(); + }; + + value("") buffer; + + backtrack_point() read_point; + rpipe->read() data; + + If (data.not_eof) { + buffer->append(data); + + explode("\n", buffer, "2") exp; + value(exp) exp; + + num_greater(exp.length, "1") found; + If (found) { + exp->get("0") address; + dbus_address->set(address); + finished_blocker->up(); + }; + + read_point->go(); + }; + + println("dbus_start: read pipe eof"); +} diff --git a/external/badvpn_dns/ncd/examples/dhcpd.conf.template b/external/badvpn_dns/ncd/examples/dhcpd.conf.template new file mode 100644 index 00000000..9ce5ad28 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/dhcpd.conf.template @@ -0,0 +1,11 @@ +default-lease-time 600; +max-lease-time 7200; +log-facility local7; +ddns-update-style none; +local-address ; + +subnet netmask { + range ; + option routers ; + option domain-name-servers ; +} diff --git a/external/badvpn_dns/ncd/examples/directory_updater.ncd b/external/badvpn_dns/ncd/examples/directory_updater.ncd new file mode 100644 index 00000000..cb546ff9 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/directory_updater.ncd @@ -0,0 +1,72 @@ +# +# Watches a directory and runs an update program whenever something +# changes. This is race-free, and if the directory changes while the +# update is running, it will be done again. +# +# NOTE: the update should not modify the directory. If it does, the +# the result will be endless and constant updating. +# + +process watcher { + # Blocker object used to trigger an update. + blocker() blk; + + # State: updating - if updater script is running + # updating_dirty - if something changed while + # script was running + var("false") updating; + var("false") updating_dirty; + + # Start update process. + spawn("updater", {}); + + # Wait for directory event. + # CHANGE THIS + sys.watch_directory("/home/ambro") watcher; + + # Print event details (e.g. "added somefile"). + println(watcher.filename, " ", watcher.event_type); + + # If updating is in progress, mark dirty. + If (updating) { + updating_dirty->set("true"); + }; + + # Request update. This makes use() proceed forward. + blk->up(); + + # Wait for next event (execution moves up). + watcher->nextevent(); +} + +template updater { + # Wait for update request. + _caller.blk->use(); + + # Wait some time. + sleep("1000", "0"); + + # We're about to start update script - set updating + # variable and mark as non dirty. + _caller.updating_dirty->set("false"); + _caller.updating->set("true"); + + println("Update started"); + + # CHANGE THIS + sleep("3000", "0"); + #runonce({"/bin/echo", "Updater speaking"}, {"keep_stdout", "keep_stderr"}); + + println("Update finished"); + + # No longer running update script. + _caller.updating->set("false"); + + # If something changed while script was running, restart + # update; else wait for next update request. + If (_caller.updating_dirty) { + _caller.blk->downup(); + } else { + _caller.blk->down(); + }; +} diff --git a/external/badvpn_dns/ncd/examples/events.ncd b/external/badvpn_dns/ncd/examples/events.ncd new file mode 100644 index 00000000..9fe80a8d --- /dev/null +++ b/external/badvpn_dns/ncd/examples/events.ncd @@ -0,0 +1,101 @@ +# +# NCD input event handling example program. +# +# This program responds to volume key presses by synchronously calling an external +# script for muting and adjusting volume, and responds to power button presses by +# suspending using pm-suspend. +# +# It uses process_manager() and sys.watch_input() to dynamically create and remove +# processes that deal with specific input devices. The individual input device processes +# then use sys.evdev() to handle input events from their input device. +# + +process events_main { + # Volume control script, called with argument "up", "down" or "mute". + var("/usr/local/bin/volumekey") volume_script; + + # Suspend command. + list("/usr/sbin/pm-suspend") suspend_cmd; + + provide("GLOBAL"); +} + +process events_watcher { + depend("GLOBAL"); + + # Create process manager. + process_manager() manager; + + # Wait for input device events. + sys.watch_input("event") watcher; + + # Dispatch. + concat("events_watcher_", watcher.event_type) func; + call(func, {}); + + # Next event. + watcher->nextevent(); +} + +template events_watcher_added { + # Start event handling process for this device. + _caller.manager->start(_caller.watcher.devname, "events_input_device", {_caller.watcher.devname}); +} + +template events_watcher_removed { + # Stop event handling process for this device. + _caller.manager->stop(_caller.watcher.devname); +} + +template events_input_device { + # Alias arguments. + var(_arg0) dev; + + # Get global. + depend("GLOBAL") gdep; + + # Wait for input events. + sys.evdev(dev) evdev; + + # Query event details. + strcmp(evdev.code, "KEY_MUTE") is_mute; + strcmp(evdev.code, "KEY_VOLUMEUP") is_vup; + strcmp(evdev.code, "KEY_VOLUMEDOWN") is_vdown; + strcmp(evdev.code, "KEY_POWER") is_power; + strcmp(evdev.value, "1") is_pressed; + + # Compute where to dispatch the event. + and(is_mute, is_pressed) dispatch_mute; + and(is_vup, is_pressed) dispatch_vup; + and(is_vdown, is_pressed) dispatch_vdown; + and(is_power, is_pressed) dispatch_power; + + # Dispatch event. + choose({ + {dispatch_mute, "events_input_event_mute"}, + {dispatch_vup, "events_input_event_vup"}, + {dispatch_vdown, "events_input_event_vdown"}, + {dispatch_power, "events_input_event_power"}}, + "" + ) func; + call(func, {}); + + # Next event. + evdev->nextevent(); +} + +template events_input_event_mute { + runonce({_caller.gdep.volume_script, "mute"}); +} + +template events_input_event_vup { + runonce({_caller.gdep.volume_script, "up"}); +} + +template events_input_event_vdown { + runonce({_caller.gdep.volume_script, "down"}); +} + +template events_input_event_power { + runonce(_caller.gdep.suspend_cmd); +} diff --git a/external/badvpn_dns/ncd/examples/igmpproxy.conf.template b/external/badvpn_dns/ncd/examples/igmpproxy.conf.template new file mode 100644 index 00000000..9e5a83cb --- /dev/null +++ b/external/badvpn_dns/ncd/examples/igmpproxy.conf.template @@ -0,0 +1,10 @@ +quickleave + +phyint upstream ratelimit 0 threshold 1 + altnet 89.143.8.0/24 + altnet 95.176.0.0/16 + +phyint downstream ratelimit 0 threshold 1 + +# A lot of these will be appended by make_igmpproxy_config: +# phyint eth0 disabled diff --git a/external/badvpn_dns/ncd/examples/make_dhcp_config.ncd b/external/badvpn_dns/ncd/examples/make_dhcp_config.ncd new file mode 100644 index 00000000..201a0529 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/make_dhcp_config.ncd @@ -0,0 +1,27 @@ +process main { + call("make_dhcp_config", {"192.168.1.1", "24", "192.168.1.100", "192.168.1.199", {"192.168.1.1"}, {"192.168.1.1", "4.4.4.4"}, "dhcpd.conf.template", "dhcpd.conf"}) config; + exit("0"); +} + +template make_dhcp_config { + alias("_arg0") addr; + alias("_arg1") prefix; + alias("_arg2") range_start; + alias("_arg3") range_end; + alias("_arg4") routers; + alias("_arg5") dns_servers; + alias("_arg6") template_file; + alias("_arg7") output_file; + + ipv4_net_from_addr_and_prefix(addr, prefix) network; + ipv4_prefix_to_mask(prefix) netmask; + implode(", ", routers) routers_str; + implode(", ", dns_servers) dns_servers_str; + + var({"", "", "", "", "", "", ""}) regex; + var({addr, network, netmask, range_start, range_end, routers_str, dns_servers_str}) replace; + + file_read(template_file) template_data; + regex_replace(template_data, regex, replace) replaced_data; + file_write(output_file, replaced_data); +} diff --git a/external/badvpn_dns/ncd/examples/make_igmpproxy_config.ncd b/external/badvpn_dns/ncd/examples/make_igmpproxy_config.ncd new file mode 100644 index 00000000..77ca0a91 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/make_igmpproxy_config.ncd @@ -0,0 +1,53 @@ +process main { + call("make_igmpproxy_config", {"br0", "eth1", "./igmpproxy.conf.template", "./igmpproxy.conf"}); + + println("Done."); + exit("0"); +} + +template make_igmpproxy_config { + alias("_arg0") upstream_iface; + alias("_arg1") downstream_iface; + alias("_arg2") template_file; + alias("_arg3") output_file; + + # Make a list of interfaces to add as disabled (upstream and downstream will not be disabled). + var({ + "eth0", "eth1", "eth2", "eth3", "eth4", "eth5", "eth6", "eth7", "eth8", "eth9", + "wlan0", "wlan1", "wlan2", "wlan3", "wlan4", "wlan5", "wlan6", "wlan7", "wlan8", "wlan9", + "tap0", "tap1", "tap2", "tap3", "tap4", "tap5", "tap6", "tap7", "tap8", "tap9", + "br0", "br1", "br2", "br3", "br4", "br5", "br6", "br7", "br8", "br9" + }) disabled_ifaces; + + # Build replacements for template config. + var({"", ""}) regex; + var({upstream_iface, downstream_iface}) replace; + + # Read template config. + file_read(template_file) template_data; + + # Perform replacements. + regex_replace(template_data, regex, replace) replaced_data; + + # Build string value from replaced_data, to which we will append + # configuration for disabled interfaces. + value(replaced_data) output_data; + + # Build disabled strings. + Foreach (disabled_ifaces As iface) { + # Determine if the interface is disabled. + val_equal(iface, upstream_iface) is_up; + val_equal(iface, downstream_iface) is_down; + or(is_up, is_down) is_not_disabled; + not(is_not_disabled) is_disabled; + + # If it's disabled, append to the configuration file. + If (is_disabled) { + concat("phyint ", iface, " disabled\n") str; + output_data->append(str); + }; + }; + + # Write config. + file_write(output_file, output_data); +} diff --git a/external/badvpn_dns/ncd/examples/network.ncd b/external/badvpn_dns/ncd/examples/network.ncd new file mode 100644 index 00000000..2eaeb8fd --- /dev/null +++ b/external/badvpn_dns/ncd/examples/network.ncd @@ -0,0 +1,123 @@ +# An example NCD script for network configuration. +# +# The first three processes demonstrate different kinds of interfaces +# and configurations. They are all disabled by default. +# +# The last process waits for one of the interfaces to come up +# and sets up routes and DNS entries to use that interface for +# Internet access. +# +# Be sure to change the dependency list in the last process to name +# the interfaces you use. + +# Example wired interface with static configuration. +process wired_example_static { + if("false"); # remove/comment to enable + + # Set device. + var("eth0") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Static configuration. + var("192.168.111.116") addr; + var("24") addr_prefix; + var("192.168.111.1") gateway; + var({"192.168.111.14", "193.2.1.66"}) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# Example wired interface with DHCP configuration. +process wired_example_dhcp { + if("false"); # remove/comment to enable + + # Set device. + var("eth1") dev; + + # Wait for device. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# Example wireless interface with DHCP configuration. +# This will use the wpa_supplicant configuration file /etc/wpa_supplicant/all.conf +# which should specify the wireless networks and other options. +process wireless_example_dhcp { + if("false"); # remove/comment to enable + + # Set device. + var("wlan0") dev; + + # Wait for device and rfkill. + net.backend.waitdevice(dev); + net.backend.rfkill("wlan", dev); + + # Connect to wireless network. + net.backend.wpa_supplicant(dev, "/etc/wpa_supplicant/all.conf", "/usr/sbin/wpa_supplicant", {}); + + # DHCP configuration. + net.ipv4.dhcp(dev) dhcp; + ip_in_network(dhcp.addr, "127.0.0.0", "8") test_local; + ifnot(test_local); + var(dhcp.addr) addr; + var(dhcp.prefix) addr_prefix; + var(dhcp.gateway) gateway; + var(dhcp.dns_servers) dns_servers; + + # Assign IP address. + net.ipv4.addr(dev, addr, addr_prefix); + + # Go on configuring the network. + concat("NET-", dev) provide_name; + multiprovide(provide_name); +} + +# This process sets up routes and DNS servers for at most one of +# the working interfaces. It will change the configuration if a +# more important interface comes up while one is already up. +process NETCONF { + # Choose devices and priorities; put preferred devices to the front. + var({"NET-eth0", "NET-eth1", "NET-wlan0"}) provide_names; + + # Wait for one of the interfaces (and deinit/switch appropriately). + multidepend(provide_names) ifdep; + + # Alias device values. + var(ifdep.dev) dev; + var(ifdep.addr) addr; + var(ifdep.addr_prefix) addr_prefix; + var(ifdep.gateway) gateway; + var(ifdep.dns_servers) dns_servers; + + # Add default route. + net.ipv4.route("0.0.0.0", "0", gateway, "20", dev); + + # Configure DNS servers. + net.dns(dns_servers, "20"); +} diff --git a/external/badvpn_dns/ncd/examples/onoff_server.ncdi b/external/badvpn_dns/ncd/examples/onoff_server.ncdi new file mode 100644 index 00000000..a380cdef --- /dev/null +++ b/external/badvpn_dns/ncd/examples/onoff_server.ncdi @@ -0,0 +1,82 @@ +include_guard "onoff_server" + +template onoff_server_main { + alias("_arg0") socket_path; + + depend_scope() depsc; + process_manager() mgr; + + sys.request_server({"unix", socket_path}, "onoff_server__request_handler", {}); +} + +template onoff_server_onoff { + alias(_arg0) main; + alias("_arg1") onoff_id; + alias("_arg2") default_state; + + main.mgr->start(onoff_id, "onoff_server__id_proc", {onoff_id, default_state}); + main.depsc->depend({onoff_id}) id_proc; + + id_proc.blk->use(); +} + +template onoff_server__id_proc { + alias("_caller") main; + alias("_arg0") onoff_id; + alias("_arg1") default_state; + + blocker() blk; + If (default_state) { + blk->up(); + }; + main.depsc->provide(onoff_id); +} + +template onoff_server__request_handler { + alias("_caller") main; + + try("onoff_server__try_request", {}); + + _request->reply({"error", "Bad request."}); + _request->finish(); +} + +template onoff_server__try_request { + alias("_caller.main") main; + alias("_caller._request") request; + + value(request.data) data; + + val_equal(data.type, "list") a; + _try->assert(a); + + num_greater_equal(data.length, "1") a; + _try->assert(a); + + data->get("0") request_cmd; + val_equal(request_cmd, "set_onoff") is_set_onoff; + + If (is_set_onoff) { + num_equal(data.length, "3") a; + _try->assert(a); + + data->get("1") onoff_id; + data->get("2") new_state; + + main.mgr->start(onoff_id, "onoff_server__id_proc", {onoff_id, new_state}); + main.depsc->depend({onoff_id}) id_proc; + + val_equal(new_state, "true") is_on; + If (is_on) { + id_proc.blk->up(); + } Else { + id_proc.blk->down(); + }; + } + Else { + _try->assert("false"); + }; + + request->reply({"OK", "state has been set"}); + request->finish(); +} diff --git a/external/badvpn_dns/ncd/examples/onoff_server_test.ncd b/external/badvpn_dns/ncd/examples/onoff_server_test.ncd new file mode 100644 index 00000000..7ff0b160 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/onoff_server_test.ncd @@ -0,0 +1,20 @@ +include "onoff_server.ncdi" + +process main { + call("onoff_server_main", {"/home/ambro/onoff.socket"}) onoff_server; + + process_manager() mgr; + mgr->start("service1", {}); + #mgr->start("service2", {}); +} + +template service1 { + alias("_caller") main; + + call("onoff_server_onoff", {"_caller.main.onoff_server", "ServiceId1", "true"}); + + println("service1 up"); + rprintln("service1 down"); + + # Do your stuff. +} diff --git a/external/badvpn_dns/ncd/examples/router/README b/external/badvpn_dns/ncd/examples/router/README new file mode 100644 index 00000000..7be37430 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/README @@ -0,0 +1,36 @@ +NCD Router Example + +-- Operation --- + +These are the NCD scripts I run on my home router. +Three network interfaces are being configured: + +1. The LAN interface. +The DHCP server is started for this interface, and also a DNS server (unbound). +2. The Internet interface. +This is a PPPoE interface with NAT. +3. The ServerIf interface. +This one behaves similarly to the LAN interface, except that there is no DHCP server. +The intention is to put servers here so you can restrict communication not only between Internet and the servers, +but also between LAN and the servers (though this configuration doesn't actually do the latter). + +Hosts on the LAN and ServerIf interfaces can access the Internet, and source NAT is used here. +Additionally, it is possible to add port forwardings (DNAT) from the Internet interface to either +of those two interfaces. These can be managed with the scripts {list,add,remove}-port-forwarding. +The list of port forwarding is stored in the file /var/lib/ncd-port-forwardings.ncdvalue. +However, you should NOT modify this file while NCD is running. You should not modify it at all, because +NCD may accidentally overwrite your changes. Just use the scripts. + +Iptables is used to filter incoming connections from the Internet interface. +Exceptions can be added; for example, there's a commented line in template network_internet_pppoe_preup which allows access to the local SSH server. +To allow access to servers running on other hosts (LAN or ServerIf interface), a port forwarding should be added dynamically. + +-- Installation -- + +The following pppd patch is required for PPPoE to work: +https://code.google.com/p/ambro-gentoo-overlay/source/browse/trunk/net-dialup/ppp/files/pppd-configurable-paths.patch + +Copy ncd.conf to /etc/, and copy all other files here into a new directory /etc/ncd-network. +Explanation: ncd.conf just loads network.ncdi, which is where the bulk of the configuration is defined. +Make the {list,add,remove}-port-forwarding scripts executable. Additionally, if your NCD interpreter is not located at /usr/bin/badvpn-ncd, +adjust the interpreter paths inside them. diff --git a/external/badvpn_dns/ncd/examples/router/add-port-forwarding b/external/badvpn_dns/ncd/examples/router/add-port-forwarding new file mode 100644 index 00000000..5d8a5086 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/add-port-forwarding @@ -0,0 +1,43 @@ +#!/usr/bin/badvpn-ncd + +process main { + getargs() args; + value(args) args; + + num_different(args.length, "4") bad_args; + If (bad_args) { + println("Usage: add-port-forwarding "); + exit("1"); + }; + + args->get("0") protocol; + args->get("1") port_start; + args->get("2") port_end; + args->get("3") dest_addr; + + var("0") exit_status; + + sys.request_client({"unix", "/run/ncd-control.socket"}) client; + + var({"add-port-forwarding", protocol, port_start, port_end, dest_addr}) request_data; + + client->request(request_data, "reply_handler", "finished_handler", {}); +} + +template reply_handler { + value(_reply.data) reply_data; + reply_data->get("0") status; + reply_data->get("1") text; + + val_equal(status, "ok") is_ok; + If (is_ok) { + println(text); + } Else { + _caller.exit_status->set("1"); + println("Error: ", text); + }; +} + +template finished_handler { + exit(_caller.exit_status); +} diff --git a/external/badvpn_dns/ncd/examples/router/dhcp_server.ncdi b/external/badvpn_dns/ncd/examples/router/dhcp_server.ncdi new file mode 100644 index 00000000..98c25438 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/dhcp_server.ncdi @@ -0,0 +1,60 @@ +include_guard "dhcp_server" + +template dhcp_server { + alias("_arg0") addr; + alias("_arg1") prefix; + alias("_arg2") range_start; + alias("_arg3") range_end; + alias("_arg4") routers; + alias("_arg5") dns_servers; + + # Choose lease file. + concat("/var/lib/dhcp/dhcpd-", addr, ".leases") leases_file; + + # Create leases file if it doesn't exist. + file_stat(leases_file) stat; + If (stat.succeeded) { print(); } Else { + file_write(leases_file, ""); + }; + + # Create a temporary directory. + concat("/run/ncd-dhcp-server-", addr) run_dir; + run({"/bin/rm", "-rf", run_dir}, {}); + run({"/bin/mkdir", run_dir}, {"/bin/rm", "-rf", run_dir}); + + # Compute path for dhcp.conf. + concat(run_dir, "/dhcp.conf") dhcp_conf_path; + + # This is a template for dhcp.conf. + var(" +default-lease-time 43200; +max-lease-time 43200; +log-facility local7; +ddns-update-style none; +local-address ; + +subnet netmask { + authoritative; + range ; + option routers ; + option domain-name-servers ; +} +" ) config_template; + + # Compute some of the variables. + ipv4_net_from_addr_and_prefix(addr, prefix) network; + ipv4_prefix_to_mask(prefix) netmask; + implode(", ", routers) routers_str; + implode(", ", dns_servers) dns_servers_str; + + # Perform substitutions. + var({"", "", "", "", "", "", ""}) regex; + var({addr, network, netmask, range_start, range_end, routers_str, dns_servers_str}) replace; + regex_replace(config_template, regex, replace) config_data; + + # Write dhcp.conf. + file_write(dhcp_conf_path, config_data); + + # Start dhcpd. + daemon({"/usr/sbin/dhcpd", "-f", "-cf", dhcp_conf_path, "-user", "dhcp", "-group", "dhcp", "--no-pid", "-lf", leases_file}); +} diff --git a/external/badvpn_dns/ncd/examples/router/list-port-forwardings b/external/badvpn_dns/ncd/examples/router/list-port-forwardings new file mode 100644 index 00000000..3244c567 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/list-port-forwardings @@ -0,0 +1,61 @@ +#!/usr/bin/badvpn-ncd + +process main { + getargs() args; + value(args) args; + + num_different(args.length, "0") bad_args; + If (bad_args) { + println("Usage: list-port-forwardings"); + exit("1"); + }; + + var("0") exit_status; + + sys.request_client({"unix", "/run/ncd-control.socket"}) client; + + var({"list-port-forwardings"}) request_data; + + client->request(request_data, "reply_handler", "finished_handler", {}); +} + +template reply_handler { + value(_reply.data) reply_data; + reply_data->get("0") status; + reply_data->get("1") arg; + + val_equal(status, "ok") is_ok; + If (is_ok) { + println("Protocol Start End Destination"); + Foreach (arg As entry) { + value(entry) entry; + entry->get("0") protocol; + entry->get("1") port_start; + entry->get("2") port_end; + entry->get("3") dest_addr; + call("append_spaces", {port_start, "5"}) fixed_start; + call("append_spaces", {port_end, "5"}) fixed_end; + println(protocol, " ", fixed_start.result, " ", fixed_end.result, " ", dest_addr); + }; + } Else { + _caller.exit_status->set("1"); + println("Error: ", arg); + }; +} + +template finished_handler { + exit(_caller.exit_status); +} + +template append_spaces { + alias("_arg0") input; + alias("_arg1") min_length; + + value(input) result; + backtrack_point() point; + num_lesser(result.length, min_length) more; + If (more) { + result->append(" "); + point->go(); + }; +} diff --git a/external/badvpn_dns/ncd/examples/router/ncd.conf b/external/badvpn_dns/ncd/examples/router/ncd.conf new file mode 100644 index 00000000..56f6a73a --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/ncd.conf @@ -0,0 +1,6 @@ +include "/etc/ncd-network/network.ncdi" + +process main { + process_manager() mgr; + mgr->start("network_main", {}); +} diff --git a/external/badvpn_dns/ncd/examples/router/network.ncdi b/external/badvpn_dns/ncd/examples/router/network.ncdi new file mode 100644 index 00000000..f2a02cd5 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/network.ncdi @@ -0,0 +1,356 @@ +include_guard "network" + +include "pppoe.ncdi" +include "dhcp_server.ncdi" +include "unbound.ncdi" +include "network_control_server.ncdi" +include "port_forwarding.ncdi" + +template network_main { + log("notice", "NCD starting"); + log_r("notice", "NCD stopped"); + + # Load ipv6 module so we can disable ipv6. + runonce({"/sbin/modprobe", "ipv6"}); + + # Set some sysctl's. + runonce({"/sbin/sysctl", "net.ipv4.ip_forward=1"}); + runonce({"/sbin/sysctl", "net.ipv6.conf.all.disable_ipv6=1"}); + + # Setup iptables INPUT chain. + net.iptables.policy("filter", "INPUT", "ACCEPT", "ACCEPT"); + net.iptables.append("filter", "INPUT", "-i", "lo", "-j", "ACCEPT"); + net.iptables.append("filter", "INPUT", "-m", "conntrack", "--ctstate", "ESTABLISHED", "-j", "ACCEPT"); + + # Setup iptables OUTPUT chain. + net.iptables.policy("filter", "OUTPUT", "ACCEPT", "ACCEPT"); + + # Setup iptables FORWARD chain. + net.iptables.policy("filter", "FORWARD", "DROP", "ACCEPT"); + net.iptables.append("filter", "FORWARD", "-m", "conntrack", "--ctstate", "ESTABLISHED", "-j", "ACCEPT"); + net.iptables.append("filter", "FORWARD", "-m", "connmark", "--mark", "0x1/0x1", "-j", "ACCEPT"); + + # Create dependency scope. + depend_scope() depsc; + + # Start processes. + process_manager() mgr; + mgr->start("network_lan", {}); + mgr->start("network_serverif", {}); + mgr->start("network_internet", {}); + mgr->start("network_lan_internet_rules", {}); + mgr->start("network_serverif_internet_rules", {}); + mgr->start("network_lan_serverif_rules", {}); + mgr->start("network_lan_dhcp_server", {}); + mgr->start("network_unbound", {}); + mgr->start("network_port_forwarding", {}); + mgr->start("network_start_control_server", {}); +} + +template network_weak_hostmodel_rules { + alias("_arg0") dev; + alias("_arg1") addr; + + concat("INPUT_hostmodel_drop_", dev) drop_chain; + + net.iptables.newchain("filter", drop_chain); + net.iptables.append("filter", drop_chain, "-j", "DROP"); + net.iptables.append("filter", "INPUT", "-d", addr, "!", "-i", dev, "-j", drop_chain); +} + +template network_weak_hostmodel_exception { + alias("_arg0") dev; + alias("_arg1") match; + + concat("INPUT_hostmodel_drop_", dev) drop_chain; + + listfrom({"filter", drop_chain}, match, {"-j", "RETURN"}) args; + net.iptables.insert(args); +} + +template network_lan { + alias("_caller") main; + + # Some configuration. + var("enp1s0") dev; + var("192.168.111.1") addr; + var("24") prefix; + var("192.168.111.100") dhcp_start; + var("192.168.111.149") dhcp_end; + + main.depsc->provide("lan_config"); + + # Wait for device, set up, wait for link. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Weak host model. + call("network_weak_hostmodel_rules", {dev, addr}); + + # Assign IP address. + net.ipv4.addr(dev, addr, prefix); + + # Do SNAT for port forwardings when connections originate from the inside. + net.iptables.append("nat", "POSTROUTING", "-m", "connmark", "--mark", "0x2/0x2", "-j", "SNAT", "--to-source", addr, "--random"); + + main.depsc->provide("lan"); +} + +template network_serverif { + alias("_caller") main; + + # Some configuration. + var("enp3s0") dev; + var("192.168.113.1") addr; + var("24") prefix; + + main.depsc->provide("serverif_config"); + + # Wait for device, set up, wait for link. + net.backend.waitdevice(dev); + net.up(dev); + net.backend.waitlink(dev); + + # Weak host model. + call("network_weak_hostmodel_rules", {dev, addr}); + + # Assign IP address. + net.ipv4.addr(dev, addr, prefix); + + # Do SNAT for port forwardings when connections originate from the inside. + net.iptables.append("nat", "POSTROUTING", "-m", "connmark", "--mark", "0x4/0x4", "-j", "SNAT", "--to-source", addr, "--random"); + + main.depsc->provide("serverif"); +} + +template network_internet { + alias("_caller") main; + + # Some configuration. + var("enp2s0") pppoe_dev; + var("MISSING") pppoe_username; + var("MISSING") pppoe_password; + + # Wait for device, set up, wait for link. + net.backend.waitdevice(pppoe_dev); + net.up(pppoe_dev); + net.backend.waitlink(pppoe_dev); + + log("notice", "PPPoE started"); + log_r("notice", "PPPoE stopped"); + + # Start PPPoE. + call("pppoe", {pppoe_dev, pppoe_username, pppoe_password, "network_internet_pppoe_preup"}) pppoe; + + # Grab configuration. + var(pppoe.ifname) dev; + var(pppoe.local_ip) addr; + var(pppoe.remote_ip) remote_addr; + var(pppoe.dns_servers) dns_servers; + + to_string(dns_servers) dns_str; + log("notice", "PPPoE up dev=", dev, " local=", addr, " remote=", remote_addr, " dns=", dns_str); + log_r("notice", "PPPoE down"); + + # Add default route. + net.ipv4.route("0.0.0.0/0", remote_addr, "20", dev); + + main.depsc->provide("internet"); +} + +template network_internet_pppoe_preup { + alias("_arg0") dev; + alias("_arg1") addr; + alias("_arg2") remote_ip; + alias("_arg3") dns_servers; + + # Weak host model. + call("network_weak_hostmodel_rules", {dev, addr}); + + # Drop packets to this system, except some things. + net.iptables.newchain("filter", "INPUT_internet_drop"); + #net.iptables.append("filter", "INPUT_internet_drop", "-p", "tcp", "--dport", "22", "-j", "RETURN"); + net.iptables.append("filter", "INPUT_internet_drop", "-j", "DROP"); + net.iptables.append("filter", "INPUT", "-i", dev, "-j", "INPUT_internet_drop"); + + # Do SNAT for packets going out. + net.iptables.append("nat", "POSTROUTING", "-o", dev, "-j", "SNAT", "--to-source", addr, "--random"); + + # Do MMS clamping. + net.iptables.append("mangle", "OUTPUT", "-o", dev, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"); + net.iptables.append("mangle", "FORWARD", "-o", dev, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"); +} + +template network_lan_internet_rules { + alias("_caller") main; + main.depsc->depend({"lan"}) lan; + main.depsc->depend({"internet"}) internet; + + # Add exception to weak host model of internet interface. + call("network_weak_hostmodel_exception", {internet.dev, {"-i", lan.dev}}); + net.iptables.append("filter", "FORWARD", "-m", "conntrack", "--ctstate", "NEW", "-i", lan.dev, "-o", internet.dev, "-j", "ACCEPT"); +} + +template network_serverif_internet_rules { + alias("_caller") main; + main.depsc->depend({"serverif"}) serverif; + main.depsc->depend({"internet"}) internet; + + # Allow traffic from LAN to Internet. + call("network_weak_hostmodel_exception", {internet.dev, {"-i", serverif.dev}}); + net.iptables.append("filter", "FORWARD", "-m", "conntrack", "--ctstate", "NEW", "-i", serverif.dev, "-o", internet.dev, "-j", "ACCEPT"); +} + +template network_lan_serverif_rules { + alias("_caller") main; + main.depsc->depend({"lan"}) lan; + main.depsc->depend({"serverif"}) serverif; + + # Allow traffic from serverif to LAN. + call("network_weak_hostmodel_exception", {serverif.dev, {"-i", lan.dev}}); + net.iptables.append("filter", "FORWARD", "-m", "conntrack", "--ctstate", "NEW", "-i", lan.dev, "-o", serverif.dev, "-j", "ACCEPT"); + + # Allow traffic from LAN to serverif. + call("network_weak_hostmodel_exception", {lan.dev, {"-i", serverif.dev}}); + net.iptables.append("filter", "FORWARD", "-m", "conntrack", "--ctstate", "NEW", "-i", serverif.dev, "-o", lan.dev, "-j", "ACCEPT"); +} + +template network_lan_dhcp_server { + alias("_caller") main; + main.depsc->depend({"lan"}) lan; + + # Start DHCP server. + call("dhcp_server", {lan.addr, lan.prefix, lan.dhcp_start, lan.dhcp_end, {lan.addr}, {lan.addr}}); +} + +template network_unbound { + alias("_caller") main; + main.depsc->depend({"lan_config"}) lan_config; + main.depsc->depend({"serverif_config"}) serverif_config; + + # Add DNS servers. + net.dns({"127.0.0.1"}, "20"); + + # Build configuration. + ipv4_net_from_addr_and_prefix(lan_config.addr, lan_config.prefix) lan_network; + ipv4_net_from_addr_and_prefix(serverif_config.addr, serverif_config.prefix) serverif_network; + var({ + {lan_network, lan_config.prefix, "allow"}, + {serverif_network, serverif_config.prefix, "allow"} + }) access_control_rules; + + # Start Unbound. + call("unbound", {"lan", access_control_rules}); +} + +template network_port_forwarding { + alias("_caller") main; + + # Start forwarding. + call("port_forwarding", {"/var/lib/ncd-port-forwardings.ncdvalue", "network_port_forwarding_rules"}) pf; + + main.depsc->provide("port_forwarding"); +} + +template network_port_forwarding_rules { + alias("_caller.main") main; + alias("_arg0") protocol; + alias("_arg1") port_start; + alias("_arg2") port_end; + alias("_arg3") dest_addr; + + # Get access to lan and serverif configuration. + main.depsc->depend({"lan_config"}) lan; + main.depsc->depend({"serverif_config"}) serverif; + + # Wait for Internet interface. + main.depsc->depend({"internet"}) internet; + + # Build port range string. + concat(port_start, ":", port_end) port_range; + + # Add rules. + net.iptables.append("nat", "PREROUTING", "-d", internet.addr, "-p", protocol, "--dport", port_range, "-i", lan.dev, "-j", "CONNMARK", "--set-xmark", "0x3/0x3"); + net.iptables.append("nat", "PREROUTING", "-d", internet.addr, "-p", protocol, "--dport", port_range, "-i", serverif.dev, "-j", "CONNMARK", "--set-xmark", "0x5/0x5"); + net.iptables.append("nat", "PREROUTING", "-d", internet.addr, "-p", protocol, "--dport", port_range, "-i", internet.dev, "-j", "CONNMARK", "--set-xmark", "0x1/0x1"); + net.iptables.append("nat", "PREROUTING", "-d", internet.addr, "-p", protocol, "--dport", port_range, "-j", "DNAT", "--to-destination", dest_addr); +} + +template network_start_control_server { + alias("_caller") main; + main.depsc->depend({"lan_config"}) lan_config; + + # Start control server. + call("network_control_server", {"/run/ncd-control.socket", + "network_control_list_port_forwardings", + "network_control_add_port_forwarding", + "network_control_remove_port_forwarding"}); +} + +template network_control_list_port_forwardings { + alias("_caller.main") main; + + main.depsc->depend({"port_forwarding"}) port_forwarding; + var(port_forwarding.pf.map.keys) port_forwardings; +} + +template network_control_add_port_forwarding { + alias("_caller.main") main; + alias("_arg0") protocol; + alias("_arg1") port_start; + alias("_arg2") port_end; + alias("_arg3") dest_addr; + + var("") try_error_text; + try("network_verify_port_forwarding_try", {}) verify_try; + + If (verify_try.succeeded) { + main.depsc->depend({"port_forwarding"}) port_forwarding; + + call("port_forwarding_add", {"_caller.port_forwarding.pf", protocol, port_start, port_end, dest_addr}) call; + alias("call.succeeded") succeeded; + alias("call.error_text") error_text; + } Else { + var("false") succeeded; + alias("try_error_text") error_text; + } branch; + + alias("branch.succeeded") succeeded; + alias("branch.error_text") error_text; +} + +template network_control_remove_port_forwarding { + alias("_caller.main") main; + alias("_arg0") protocol; + alias("_arg1") port_start; + alias("_arg2") port_end; + alias("_arg3") dest_addr; + + main.depsc->depend({"port_forwarding"}) port_forwarding; + + call("port_forwarding_remove", {"_caller.port_forwarding.pf", protocol, port_start, port_end, dest_addr}) call; + alias("call.succeeded") succeeded; + alias("call.error_text") error_text; +} + +template network_verify_port_forwarding_try { + alias("_caller") c; + + c.main.depsc->depend({"lan_config"}) lan; + c.main.depsc->depend({"serverif_config"}) serverif; + + net.ipv4.addr_in_network(c.dest_addr, lan.addr, lan.prefix) in_lan; + net.ipv4.addr_in_network(c.dest_addr, serverif.addr, serverif.prefix) in_serverif; + + If (in_lan) { + print(); + } + Elif (in_serverif) { + print(); + } + Else { + c.try_error_text->set("Destination address does not belong to any permitted network."); + _try->assert("false"); + }; +} diff --git a/external/badvpn_dns/ncd/examples/router/network_control_server.ncdi b/external/badvpn_dns/ncd/examples/router/network_control_server.ncdi new file mode 100644 index 00000000..e94f05e4 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/network_control_server.ncdi @@ -0,0 +1,96 @@ +include_guard "network_control_server" + +template network_control_server { + alias("_arg0") socket_path; + alias("_arg1") template_list_port_forwardings; + alias("_arg2") template_add_port_forwarding; + alias("_arg3") template_remove_port_forwarding; + + # Start request server. + sys.request_server({"unix", socket_path}, "network_control_server__request_handler", {}); +} + +template network_control_server__request_handler { + alias("_caller") server; + + value(_request.data) data; + + try("network_control_server__try", {}); + + _request->reply({"error", "Bad request."}); + _request->finish(); +} + +template network_control_server__try { + alias("_caller._request") request; + alias("_caller.server") server; + alias("_caller.data") data; + + val_equal(data.type, "list") a1; + _try->assert(a1); + + num_greater_equal(data.length, "1") a2; + _try->assert(a2); + + data->get("0") request_cmd; + + val_equal(request_cmd, "list-port-forwardings") is_list_port_forwardings; + val_equal(request_cmd, "add-port-forwarding") is_add_port_forwarding; + val_equal(request_cmd, "remove-port-forwarding") is_remove_port_forwarding; + or(is_add_port_forwarding, is_remove_port_forwarding) is_add_remove_port_forwarding; + + If (is_list_port_forwardings) { + num_equal(data.length, "1") a3; + _try->assert(a3); + + call_with_caller_target(server.template_list_port_forwardings, {}, "server._caller") call; + request->reply({"ok", call.port_forwardings}); + } + Elif (is_add_remove_port_forwarding) { + num_equal(data.length, "5") a4; + _try->assert(a4); + + data->get("1") req_protocol; + data->get("2") req_port_start; + data->get("3") req_port_end; + data->get("4") req_dest_addr; + + val_equal(req_protocol, "tcp") is_tcp; + val_equal(req_protocol, "udp") is_udp; + or(is_tcp, is_udp) a5; + _try->assert(a5); + + parse_number(req_port_start) port_start; + _try->assert(port_start.succeeded); + + parse_number(req_port_end) port_end; + _try->assert(port_end.succeeded); + + num_lesser_equal(port_start, port_end) a6; + _try->assert(a6); + + parse_ipv4_addr(req_dest_addr) dest_addr; + _try->assert(dest_addr.succeeded); + + If (is_add_port_forwarding) { + call_with_caller_target(server.template_add_port_forwarding, {req_protocol, port_start, port_end, dest_addr}, "server._caller") call; + If (call.succeeded) { + request->reply({"ok", "Port forwarding added."}); + } Else { + request->reply({"error", call.error_text}); + }; + } Else { + call_with_caller_target(server.template_remove_port_forwarding, {req_protocol, port_start, port_end, dest_addr}, "server._caller") call; + If (call.succeeded) { + request->reply({"ok", "Port forwarding removed."}); + } Else { + request->reply({"error", call.error_text}); + }; + }; + } + Else { + _try->assert("false"); + }; + + request->finish(); +} diff --git a/external/badvpn_dns/ncd/examples/router/port_forwarding.ncdi b/external/badvpn_dns/ncd/examples/router/port_forwarding.ncdi new file mode 100644 index 00000000..1ac727b2 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/port_forwarding.ncdi @@ -0,0 +1,170 @@ +include_guard "port_forwarding" + +template port_forwarding { + alias("_arg0") forwardings_file; + alias("_arg1") template_forward; + + # Map which holds the set of current port forwardings. + # Enties are: {protocol, port_start, port_end, dest_addr}:"" + value([]) map; + + # Blocker which is initially down and is toggled down-up + # whenever the forwarding change. + blocker() update_blocker; + + # Process manager, each forwarding has a port_forwarding__instance process. + # The process identifiers are the same as the keys in the map. + process_manager() mgr; + + # Spawn a process for dealing with storage of port forwardings on disk. + spawn("port_forwarding__stored", {}); +} + +template port_forwarding__instance { + alias("_caller") pf; + alias("_arg0") protocol; + alias("_arg1") port_start; + alias("_arg2") port_end; + alias("_arg3") dest_addr; + + log("notice", "adding port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr); + log_r("notice", "removed port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr); + + # Do the forwarding. + call_with_caller_target(pf.template_forward, {protocol, port_start, port_end, dest_addr}, "pf._caller"); +} + +template port_forwarding_add { + alias(_arg0) pf; + alias("_arg1") protocol; + alias("_arg2") port_start; + alias("_arg3") port_end; + alias("_arg4") dest_addr; + + var("false") succeeded; + var("") error_text; + var("true") not_finished; + backtrack_point() finished_point; + + If (not_finished) { + # Check for conflicts with existing forwardings. + Foreach (pf.map.keys As entry) { + value(entry) entry; + entry->get("0") e_protocol; + entry->get("1") e_port_start; + entry->get("2") e_port_end; + + val_different(protocol, e_protocol) different_protocol; + num_lesser(port_end, e_port_start) before; + num_greater(port_start, e_port_end) after; + or(different_protocol, before, after) no_conflict; + not(no_conflict) conflict; + + If (conflict) { + error_text->set("Port forwarding conflicts with an existing forwarding."); + not_finished->set("false"); + finished_point->go(); + }; + }; + + # Build entry key. + var({protocol, port_start, port_end, dest_addr}) key; + + # Insert to map and toggle blocker. + pf.map->insert(key, ""); + pf.update_blocker->downup(); + + # Start process. + pf.mgr->start(key, "port_forwarding__instance", {protocol, port_start, port_end, dest_addr}); + + succeeded->set("true"); + not_finished->set("false"); + finished_point->go(); + }; +} + +template port_forwarding_remove { + alias(_arg0) pf; + alias("_arg1") protocol; + alias("_arg2") port_start; + alias("_arg3") port_end; + alias("_arg4") dest_addr; + + var("false") succeeded; + var("") error_text; + var("true") not_finished; + backtrack_point() finished_point; + + If (not_finished) { + # Build entry key. + var({protocol, port_start, port_end, dest_addr}) key; + + # Check if the forwarding exists. + pf.map->try_get(key) entry; + not(entry.exists) does_not_exist; + If (does_not_exist) { + error_text->set("Port forwarding does not exist."); + not_finished->set("false"); + finished_point->go(); + }; + + # Stop process. + pf.mgr->stop(key); + + # Remove from map and toggle blocker. + pf.map->remove(key); + pf.update_blocker->downup(); + + succeeded->set("true"); + not_finished->set("false"); + finished_point->go(); + }; +} + +template port_forwarding__stored { + alias("_caller") pf; + + # Create file if it doesn't exist. + file_stat(pf.forwardings_file) stat; + If (stat.succeeded) { print(); } Else { + file_write(pf.forwardings_file, "{}\n"); + }; + + # Read port forwardings from file. + file_read(pf.forwardings_file) data; + from_string(data) forwardings; + + # Add them. + Foreach (forwardings As fwd) { + value(fwd) fwd; + fwd->get("0") protocol; + fwd->get("1") port_start; + fwd->get("2") port_end; + fwd->get("3") dest_addr; + call("port_forwarding_add", {"_caller.pf", protocol, port_start, port_end, dest_addr}); + }; + + # Write forwardings to file on exit. + imperative("", {}, "port_forwarding__write", {}, "6000"); + + # Also write forwardings whenever they are changed. + pf.update_blocker->use(); + call("port_forwarding__write", {}); +} + +template port_forwarding__write { + alias("_caller.pf") pf; + + # Convert forwardings to string. + to_string(pf.map.keys) data; + concat(data, "\n") data; + + # Build name of temporary file. + concat(pf.forwardings_file, ".new") temp_file; + + # Write temporary file. + file_write(temp_file, data); + + # Move to live file. + runonce({"/bin/mv", temp_file, pf.forwardings_file}); +} diff --git a/external/badvpn_dns/ncd/examples/router/pppoe.ncdi b/external/badvpn_dns/ncd/examples/router/pppoe.ncdi new file mode 100644 index 00000000..676a8fe0 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/pppoe.ncdi @@ -0,0 +1,296 @@ +include_guard "pppoe" + +template pppoe { + alias("_arg0") dev; + alias("_arg1") username; + alias("_arg2") password; + alias("_arg3") pre_up_template; + + # Choose which NCD interpreter will be used for the pppd event scripts. + var("/usr/local/badvpn/bin/badvpn-ncd") ncd_interpreter_path; + + # Retry point here. + var("false") retrying; + backtrack_point() retry_point; + If (retrying) { + sleep("5000"); + }; + retrying->set("true"); + + # Create a temporary directory. + concat("/run/ncd-pppoe-", dev) run_dir; + run({"/bin/rm", "-rf", run_dir}, {}); + run({"/bin/mkdir", run_dir}, {"/bin/rm", "-rf", run_dir}); + + # Build paths for pppd scripts and other files. + concat(run_dir, "/ncd-request.socket") socket_path; + concat(run_dir, "/pppoe.pid") pppoe_pid_path; + concat(run_dir, "/pap-secrets") pap_secrets_path; + concat(run_dir, "/chap-secrets") chap_secrets_path; + concat(run_dir, "/pppd2.tdb") pppdb_path; + concat(run_dir, "/resolv.conf") resolv_conf_path; + concat(run_dir, "/script-auth-up") path_auth_up; + concat(run_dir, "/script-auth-down") path_auth_down; + concat(run_dir, "/script-auth-fail") path_auth_fail; + concat(run_dir, "/script-ip-pre-up") path_ip_pre_up; + concat(run_dir, "/script-ip-up") path_ip_up; + concat(run_dir, "/script-ip-down") path_ip_down; + concat(run_dir, "/script-ipv6-up") path_ipv6_up; + concat(run_dir, "/script-ipv6-down") path_ipv6_down; + concat(run_dir, "/script-ipx-up") path_ipx_up; + concat(run_dir, "/script-ipx-down") path_ipx_down; + + # Write secrets files. + call("pppoe__write_secrets", {pap_secrets_path, username, password}); + call("pppoe__write_secrets", {chap_secrets_path, username, password}); + + # Write pppd scripts. These will contact us via the request socket. + call("pppoe__write_script", {"ip-pre-up", path_ip_pre_up}); + call("pppoe__write_script", {"ip-up", path_ip_up}); + call("pppoe__write_script", {"ip-down", path_ip_down}); + + # Build path arguments for pppd; + concat("pid-dir=", run_dir) arg_pid_dir; + concat("pap-secrets=", pap_secrets_path) arg_pap_secrets; + concat("chap-secrets=", chap_secrets_path) arg_chap_secrets; + concat("pppdb=", pppdb_path) arg_pppdb; + concat("resolv.conf=", resolv_conf_path) arg_resolv_conf; + concat("auth-up=", path_auth_up) arg_auth_up; + concat("auth-down=", path_auth_down) arg_auth_down; + concat("auth-fail=", path_auth_fail) arg_auth_fail; + concat("ip-pre-up=", path_ip_pre_up) arg_ip_pre_up; + concat("ip-up=", path_ip_up) arg_ip_up; + concat("ip-down=", path_ip_down) arg_ip_down; + concat("ipv6-up=", path_ipv6_up) arg_ipv6_up; + concat("ipv6-down=", path_ipv6_down) arg_ipv6_down; + concat("ipx-up=", path_ipx_up) arg_ipx_up; + concat("ipx-down=", path_ipx_down) arg_ipx_down; + + # Create state variables and blockers. When the request server + # receives requests it will update those variables and blockers. + var("down") state; + var("") current_ifname; + var("") current_local_ip; + var("") current_remote_ip; + value({}) current_dns_servers; + blocker() ip_pre_up_blocker; + blocker() ip_pre_up_done_blocker; + blocker() ip_up_blocker; + + # Start request server. + sys.request_server({"unix", socket_path}, "pppoe__request_handler", {}); + + # Start pppd. + sys.start_process({ + "/usr/sbin/pppd", "nodetach", "plugin", "rp-pppoe.so", dev, "noipdefault", "hide-password", + "usepeerdns", "user", username, + "path", arg_pid_dir, "path", arg_pap_secrets, "path", arg_chap_secrets, "path", arg_pppdb, + "path", arg_resolv_conf, + "path", arg_auth_up, "path", arg_auth_down, "path", arg_auth_fail, "path", arg_ip_pre_up, + "path", arg_ip_up, "path", arg_ip_down, "path", arg_ipv6_up, "path", arg_ipv6_down, + "path", arg_ipx_up, "path", arg_ipx_down + }, "", ["deinit_kill_time":"2000"]) pppd; + + # Start a process which will cause retrying when pppd dies. + spawn("pppoe__pppd_wait", {}); + + # Wait for ip-pre-up. + ip_pre_up_blocker->use(); + + # Grab the current state variables, so the user doesn't + # see any unexpected changes. + var(current_ifname) ifname; + var(current_local_ip) local_ip; + var(current_remote_ip) remote_ip; + var(current_dns_servers) dns_servers; + + # Call pre-up callback template. + call_with_caller_target(pre_up_template, {ifname, local_ip, remote_ip, dns_servers}, "_caller"); + + # Allow pre-up script to terminate. + ip_pre_up_done_blocker->up(); + + # Wait for connection to go up. + ip_up_blocker->use(); +} + +template pppoe__pppd_wait { + # Wait for pppd to die. + _caller.pppd->wait(); + + # Retry. + _caller.retry_point->go(); +} + +template pppoe__write_secrets { + alias("_arg0") file_path; + alias("_arg1") username; + alias("_arg2") password; + + # Escape username and password. + regex_replace(username, {"\""}, {"\\\""}) username_esc; + regex_replace(password, {"\""}, {"\\\""}) password_esc; + + # Write empty file and chmod it. + file_write(file_path, ""); + run({"/bin/chmod", "600", file_path}, {}); + + # Build contents. + concat("\"", username_esc, "\" * \"", password_esc, "\"\n") contents; + + # Write file. + file_write(file_path, contents); +} + +template pppoe__write_script { + alias("_arg0") event; + alias("_arg1") script_path; + + # This string is an NCD script which will be run by pppd. + # When run, it will contact us via the request server, + # and the requests will be processed in pppoe__request_handler. + var("#! + +process main { + # Hardcoded strings. + var(\"\") hardcoded_event; + var(\"\") hardcoded_socket; + + # Start timeout to kill us after some time if we don't manage + # to contact the server. + spawn(\"timeout_process\", {}); + + # Build event data map. + getargs() args; + value([\"EVENT\":hardcoded_event, \"ARGS\":args]) msg_map; + var({\"DEVICE\", \"IFNAME\", \"IPLOCAL\", \"IPREMOTE\", \"PEERNAME\", \"LOCALNAME\", + \"SPEED\", \"ORIG_UID\", \"PPPLOGNAME\", \"CONNECT_TIME\", \"BYTES_SENT\", + \"BYTES_RCVD\", \"LINKNAME\", \"DNS1\", \"DNS2\", \"WINS1\", \"WINS2\"}) var_names; + Foreach (var_names As var_name) { + getenv(var_name) env; + If (env.exists) { + msg_map->insert(var_name, env); + }; + }; + + # Connect to socket. + sys.request_client({\"unix\", hardcoded_socket}) client; + + # Send request. + client->request(msg_map, \"reply_handler\", \"finished_handler\", {}); +} + +template reply_handler { + print(); +} + +template finished_handler { + # Request was received by server, exit now. + exit(\"0\"); +} + +template timeout_process { + # Sleep some time. + sleep(\"5000\"); + # Timed out, exit now. + exit(\"1\"); +} + +" ) script_contents_template; + + # Replace some constants in the script with the right values. + regex_replace(script_contents_template, + {"", "", ""}, + {_caller.ncd_interpreter_path, event, _caller.socket_path} + ) script_contents; + + # Write the script. + file_write(script_path, script_contents); + + # Make it executable. + run({"/bin/chmod", "+x", script_path}, {}); +} + +template pppoe__request_handler { + alias("_caller") pppoe; + + # Get event type from request. + value(_request.data) request; + request->get("EVENT") event; + + # Match to known types. + val_equal(event, "ip-down") is_ip_down; + val_equal(event, "ip-pre-up") is_ip_pre_up; + val_equal(event, "ip-up") is_ip_up; + + If (is_ip_down) { + # Set state. + pppoe.state->set("down"); + + # Set blockers down. + pppoe.ip_up_blocker->down(); + pppoe.ip_pre_up_done_blocker->down(); + pppoe.ip_pre_up_blocker->down(); + } + Elif (is_ip_pre_up) { + # Expecting to be in "down" state here. + val_different(pppoe.state, "down") state_is_wrong; + If (state_is_wrong) { + pppoe.retry_point->go(); + _request->finish(); + }; + + # Get variables from request. + request->get("IFNAME") ifname; + request->get("IPLOCAL") local_ip; + request->get("IPREMOTE") remote_ip; + request->try_get("DNS1") dns1; + request->try_get("DNS2") dns2; + + # Write variables. + pppoe.current_ifname->set(ifname); + pppoe.current_local_ip->set(local_ip); + pppoe.current_remote_ip->set(remote_ip); + pppoe.current_dns_servers->reset({}); + If (dns1.exists) { + pppoe.current_dns_servers->insert(pppoe.current_dns_servers.length, dns1); + }; + If (dns2.exists) { + pppoe.current_dns_servers->insert(pppoe.current_dns_servers.length, dns2); + }; + + # Set state. + pppoe.state->set("pre-up"); + + # Set ip-pre-up blocker up. + pppoe.ip_pre_up_blocker->up(); + + # Wait for pre-up to be finished. This causes the script contacting + # us to not return until then, and effectively delays pppd in setting + # the device up and calling the ip-up script. + pppoe.ip_pre_up_done_blocker->use(); + } + Elif(is_ip_up) { + # Expecting to be in "pre-up" state here. + val_different(pppoe.state, "pre-up") state_is_wrong; + If (state_is_wrong) { + pppoe.retry_point->go(); + _request->finish(); + }; + + # Set state. + pppoe.state->set("up"); + + # Set ip-up blocker up. + pppoe.ip_up_blocker->up(); + }; + + # Finish request. + _request->finish(); +} + +template pppoe__escapeshellarg { + alias("_arg0") input; + regex_replace(input, {"'"}, {"\\'"}) replaced; + concat("'", replaced, "'") result; +} diff --git a/external/badvpn_dns/ncd/examples/router/remove-port-forwarding b/external/badvpn_dns/ncd/examples/router/remove-port-forwarding new file mode 100644 index 00000000..6019404e --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/remove-port-forwarding @@ -0,0 +1,43 @@ +#!/usr/bin/badvpn-ncd + +process main { + getargs() args; + value(args) args; + + num_different(args.length, "4") bad_args; + If (bad_args) { + println("Usage: remove-port-forwarding "); + exit("1"); + }; + + args->get("0") protocol; + args->get("1") port_start; + args->get("2") port_end; + args->get("3") dest_addr; + + var("0") exit_status; + + sys.request_client({"unix", "/run/ncd-control.socket"}) client; + + var({"remove-port-forwarding", protocol, port_start, port_end, dest_addr}) request_data; + + client->request(request_data, "reply_handler", "finished_handler", {}); +} + +template reply_handler { + value(_reply.data) reply_data; + reply_data->get("0") status; + reply_data->get("1") text; + + val_equal(status, "ok") is_ok; + If (is_ok) { + println(text); + } Else { + _caller.exit_status->set("1"); + println("Error: ", text); + }; +} + +template finished_handler { + exit(_caller.exit_status); +} diff --git a/external/badvpn_dns/ncd/examples/router/unbound.ncdi b/external/badvpn_dns/ncd/examples/router/unbound.ncdi new file mode 100644 index 00000000..9ea0d41d --- /dev/null +++ b/external/badvpn_dns/ncd/examples/router/unbound.ncdi @@ -0,0 +1,42 @@ +include_guard "unbound" + +template unbound { + alias("_arg0") unique_id; + alias("_arg1") access_control_rules; + + # Create a temporary directory. + concat("/run/ncd-unbound-", unique_id) run_dir; + run({"/bin/rm", "-rf", run_dir}, {}); + run({"/bin/mkdir", run_dir}, {"/bin/rm", "-rf", run_dir}); + + # Compute path for unbound.conf. + concat(run_dir, "/unbound.conf") unbound_conf_path; + + # This is a template for unbound.conf. + value(" +server: + verbosity: 1 + do-ip4: yes + do-ip6: no + do-udp: yes + do-tcp: no + interface: 0.0.0.0 + access-control: 127.0.0.0/8 allow +" ) config; + + # Append access control rules. + Foreach (access_control_rules As rule) { + value(rule) rule; + rule->get("0") network; + rule->get("1") prefix; + rule->get("2") action; + concat(" access-control: ", network, "/", prefix, " ", action, "\n") line; + config->append(line); + }; + + # Write unbound.conf. + file_write(unbound_conf_path, config); + + # Start unbound. + daemon({"/usr/sbin/unbound", "-d", "-c", unbound_conf_path}); +} diff --git a/external/badvpn_dns/ncd/examples/tcp_echo_client.ncd b/external/badvpn_dns/ncd/examples/tcp_echo_client.ncd new file mode 100644 index 00000000..47dd999c --- /dev/null +++ b/external/badvpn_dns/ncd/examples/tcp_echo_client.ncd @@ -0,0 +1,35 @@ +process main { + getargs() args; + value(args) args; + + num_different(args.length, "2") bad_args; + If (bad_args) { + println("bad arguments"); + exit("1"); + }; + + args->get("0") addr_ip; + args->get("1") addr_port; + + sys.connect({"tcp", {"ipv4", addr_ip, addr_port}}) socket; + If (socket.is_error) { + println("connection error!"); + exit("1"); + }; + + println("connected"); + + socket->write("This echo client is implemented in NCD!\n\n"); + + backtrack_point() recv_point; + + socket->read() data; + If (data.not_eof) { + socket->write(data); + recv_point->go(); + }; + + println("server disconnected"); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/examples/tcp_echo_server.ncd b/external/badvpn_dns/ncd/examples/tcp_echo_server.ncd new file mode 100644 index 00000000..a3c62670 --- /dev/null +++ b/external/badvpn_dns/ncd/examples/tcp_echo_server.ncd @@ -0,0 +1,40 @@ +process main { + getargs() args; + value(args) args; + + num_different(args.length, "2") bad_args; + If (bad_args) { + println("bad arguments"); + exit("1"); + }; + + args->get("0") addr_ip; + args->get("1") addr_port; + + sys.listen({"tcp", {"ipv4", addr_ip, addr_port}}, "client_handler", {}) listener; + If (listener.is_error) { + println("failed to listen"); + exit("1"); + }; + + println("listening"); +} + +template client_handler { + to_string(_socket.client_addr) addr_str; + + println("client ", addr_str, ": connected"); + rprintln("client ", addr_str, ": disconnected"); + + _socket->write("This echo server is implemented in NCD!\n\n"); + + backtrack_point() recv_point; + + _socket->read() data; + If (data.not_eof) { + _socket->write(data); + recv_point->go(); + }; + + _socket->close(); +} diff --git a/external/badvpn_dns/ncd/extra/BEventLock.c b/external/badvpn_dns/ncd/extra/BEventLock.c new file mode 100644 index 00000000..e65bd204 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/BEventLock.c @@ -0,0 +1,146 @@ +/** + * @file BEventLock.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "BEventLock.h" + +static void exec_job_handler (BEventLock *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->jobs)) + DebugObject_Access(&o->d_obj); + + // get job + BEventLockJob *j = UPPER_OBJECT(LinkedList1_GetFirst(&o->jobs), BEventLockJob, pending_node); + ASSERT(j->pending) + + // call handler + j->handler(j->user); + return; +} + +void BEventLock_Init (BEventLock *o, BPendingGroup *pg) +{ + // init jobs list + LinkedList1_Init(&o->jobs); + + // init exec job + BPending_Init(&o->exec_job, pg, (BPending_handler)exec_job_handler, o); + + DebugObject_Init(&o->d_obj); + DebugCounter_Init(&o->pending_ctr); +} + +void BEventLock_Free (BEventLock *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->jobs)) + DebugCounter_Free(&o->pending_ctr); + DebugObject_Free(&o->d_obj); + + // free exec jobs + BPending_Free(&o->exec_job); +} + +void BEventLockJob_Init (BEventLockJob *o, BEventLock *l, BEventLock_handler handler, void *user) +{ + // init arguments + o->l = l; + o->handler = handler; + o->user = user; + + // set not pending + o->pending = 0; + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&l->pending_ctr); +} + +void BEventLockJob_Free (BEventLockJob *o) +{ + BEventLock *l = o->l; + + DebugCounter_Decrement(&l->pending_ctr); + DebugObject_Free(&o->d_obj); + + if (o->pending) { + int was_first = (&o->pending_node == LinkedList1_GetFirst(&l->jobs)); + + // remove from jobs list + LinkedList1_Remove(&l->jobs, &o->pending_node); + + // schedule/unschedule job + if (was_first) { + if (LinkedList1_IsEmpty(&l->jobs)) { + BPending_Unset(&l->exec_job); + } else { + BPending_Set(&l->exec_job); + } + } + } +} + +void BEventLockJob_Wait (BEventLockJob *o) +{ + BEventLock *l = o->l; + ASSERT(!o->pending) + + // append to jobs + LinkedList1_Append(&l->jobs, &o->pending_node); + + // set pending + o->pending = 1; + + // schedule next job if needed + if (&o->pending_node == LinkedList1_GetFirst(&l->jobs)) { + BPending_Set(&l->exec_job); + } +} + +void BEventLockJob_Release (BEventLockJob *o) +{ + BEventLock *l = o->l; + ASSERT(o->pending) + + int was_first = (&o->pending_node == LinkedList1_GetFirst(&l->jobs)); + + // remove from jobs list + LinkedList1_Remove(&l->jobs, &o->pending_node); + + // set not pending + o->pending = 0; + + // schedule/unschedule job + if (was_first) { + if (LinkedList1_IsEmpty(&l->jobs)) { + BPending_Unset(&l->exec_job); + } else { + BPending_Set(&l->exec_job); + } + } +} diff --git a/external/badvpn_dns/ncd/extra/BEventLock.h b/external/badvpn_dns/ncd/extra/BEventLock.h new file mode 100644 index 00000000..26643189 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/BEventLock.h @@ -0,0 +1,127 @@ +/** + * @file BEventLock.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A FIFO lock for events using the job queue ({@link BPending}). + */ + +#ifndef BADVPN_BEVENTLOCK_H +#define BADVPN_BEVENTLOCK_H + +#include +#include +#include +#include + +/** + * Event context handler called when the lock job has acquired the lock + * after requesting the lock with {@link BEventLockJob_Wait}. + * The object was in waiting state. + * The object enters locked state before the handler is called. + * + * @param user as in {@link BEventLockJob_Init} + */ +typedef void (*BEventLock_handler) (void *user); + +/** + * A FIFO lock for events using the job queue ({@link BPending}). + */ +typedef struct { + LinkedList1 jobs; + BPending exec_job; + DebugObject d_obj; + DebugCounter pending_ctr; +} BEventLock; + +/** + * An object that can request a {@link BEventLock} lock. + */ +typedef struct { + BEventLock *l; + BEventLock_handler handler; + void *user; + int pending; + LinkedList1Node pending_node; + DebugObject d_obj; +} BEventLockJob; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void BEventLock_Init (BEventLock *o, BPendingGroup *pg); + +/** + * Frees the object. + * There must be no {@link BEventLockJob} objects using this lock + * (regardless of their state). + * + * @param o the object + */ +void BEventLock_Free (BEventLock *o); + +/** + * Initializes the object. + * The object is initialized in idle state. + * + * @param o the object + * @param l the lock + * @param handler handler to call when the lock is aquired + * @param user value to pass to handler + */ +void BEventLockJob_Init (BEventLockJob *o, BEventLock *l, BEventLock_handler handler, void *user); + +/** + * Frees the object. + * + * @param o the object + */ +void BEventLockJob_Free (BEventLockJob *o); + +/** + * Requests the lock. + * The object must be in idle state. + * The object enters waiting state. + * + * @param o the object + */ +void BEventLockJob_Wait (BEventLockJob *o); + +/** + * Aborts the lock request or releases the lock. + * The object must be in waiting or locked state. + * The object enters idle state. + * + * @param o the object + */ +void BEventLockJob_Release (BEventLockJob *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDBProcessOpts.c b/external/badvpn_dns/ncd/extra/NCDBProcessOpts.c new file mode 100644 index 00000000..2af4beb4 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDBProcessOpts.c @@ -0,0 +1,154 @@ +/** + * @file NCDBProcessOpts.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +#include "NCDBProcessOpts.h" + +int NCDBProcessOpts_Init (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel) +{ + return NCDBProcessOpts_Init2(o, opts_arg, func_unknown, func_unknown_user, i, blog_channel, NULL, NULL); +} + +int NCDBProcessOpts_Init2 (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel, + int *out_keep_stdout, int *out_keep_stderr) +{ + if (!NCDVal_IsInvalid(opts_arg) && !NCDVal_IsMap(opts_arg)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "options must be a map"); + goto fail0; + } + + o->username = NULL; + o->do_setsid = 0; + + int keep_stdout = 0; + int keep_stderr = 0; + + if (!NCDVal_IsInvalid(opts_arg)) { + for (NCDValMapElem me = NCDVal_MapFirst(opts_arg); !NCDVal_MapElemInvalid(me); me = NCDVal_MapNext(opts_arg, me)) { + NCDValRef key = NCDVal_MapElemKey(opts_arg, me); + NCDValRef val = NCDVal_MapElemVal(opts_arg, me); + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "keep_stdout")) { + keep_stdout = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "keep_stderr")) { + keep_stderr = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "do_setsid")) { + o->do_setsid = ncd_read_boolean(val); + } + else if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "username")) { + if (!NCDVal_IsStringNoNulls(val)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "username must be a string without nulls"); + goto fail1; + } + b_cstring cstr = NCDVal_StringCstring(val); + o->username = b_cstring_strdup(cstr, 0, cstr.length); + if (!o->username) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "b_cstring_strdup failed"); + goto fail1; + } + } + else { + if (!func_unknown || !func_unknown(func_unknown_user, key, val)) { + NCDModuleInst_Backend_Log(i, blog_channel, BLOG_ERROR, "unknown option"); + goto fail1; + } + } + } + } + + o->nfds = 0; + if (keep_stdout) { + o->fds[o->nfds] = 1; + o->fds_map[o->nfds++] = 1; + } + if (keep_stderr) { + o->fds[o->nfds] = 2; + o->fds_map[o->nfds++] = 2; + } + o->fds[o->nfds] = -1; + + if (out_keep_stdout) { + *out_keep_stdout = keep_stdout; + } + if (out_keep_stderr) { + *out_keep_stderr = keep_stderr; + } + + return 1; + +fail1: + if (o->username) { + BFree(o->username); + } +fail0: + return 0; +} + +void NCDBProcessOpts_InitOld (NCDBProcessOpts *o, int keep_stdout, int keep_stderr, int do_setsid) +{ + o->username = NULL; + o->do_setsid = do_setsid; + + o->nfds = 0; + if (keep_stdout) { + o->fds[o->nfds] = 1; + o->fds_map[o->nfds++] = 1; + } + if (keep_stderr) { + o->fds[o->nfds] = 2; + o->fds_map[o->nfds++] = 2; + } + o->fds[o->nfds] = -1; +} + +void NCDBProcessOpts_Free (NCDBProcessOpts *o) +{ + if (o->username) { + BFree(o->username); + } +} + +struct BProcess_params NCDBProcessOpts_GetParams (NCDBProcessOpts *o) +{ + struct BProcess_params params; + + params.username = o->username; + params.fds = o->fds; + params.fds_map = o->fds_map; + params.do_setsid = o->do_setsid; + + return params; +} diff --git a/external/badvpn_dns/ncd/extra/NCDBProcessOpts.h b/external/badvpn_dns/ncd/extra/NCDBProcessOpts.h new file mode 100644 index 00000000..9a9a79c9 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDBProcessOpts.h @@ -0,0 +1,54 @@ +/** + * @file NCDBProcessOpts.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BPROCESS_OPTS_H +#define NCD_BPROCESS_OPTS_H + +#include +#include +#include + +typedef struct { + char *username; + int do_setsid; + int fds[3]; + int fds_map[2]; + int nfds; +} NCDBProcessOpts; + +typedef int (*NCDBProcessOpts_func_unknown) (void *user, NCDValRef key, NCDValRef val); + +int NCDBProcessOpts_Init (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel) WARN_UNUSED; +int NCDBProcessOpts_Init2 (NCDBProcessOpts *o, NCDValRef opts_arg, NCDBProcessOpts_func_unknown func_unknown, void *func_unknown_user, NCDModuleInst *i, int blog_channel, + int *out_keep_stdout, int *out_keep_stderr) WARN_UNUSED; +void NCDBProcessOpts_InitOld (NCDBProcessOpts *o, int keep_stdout, int keep_stderr, int do_setsid); +void NCDBProcessOpts_Free (NCDBProcessOpts *o); +struct BProcess_params NCDBProcessOpts_GetParams (NCDBProcessOpts *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDBuf.c b/external/badvpn_dns/ncd/extra/NCDBuf.c new file mode 100644 index 00000000..6262652d --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDBuf.c @@ -0,0 +1,123 @@ +/** + * @file NCDBuf.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "NCDBuf.h" + +#include +#include +#include + +static void ref_target_func_release (BRefTarget *ref_target) +{ + NCDBuf *o = UPPER_OBJECT(ref_target, NCDBuf, ref_target); + NCDBufStore *store = o->store; + + if (store) { + LinkedList0_Remove(&store->used_bufs_list, &o->list_node); + LinkedList0_Prepend(&store->free_bufs_list, &o->list_node); + } else { + BFree(o); + } +} + +void NCDBufStore_Init (NCDBufStore *o, size_t buf_size) +{ + o->buf_size = buf_size; + LinkedList0_Init(&o->used_bufs_list); + LinkedList0_Init(&o->free_bufs_list); + + DebugObject_Init(&o->d_obj); +} + +void NCDBufStore_Free (NCDBufStore *o) +{ + DebugObject_Free(&o->d_obj); + + LinkedList0Node *ln; + + ln = LinkedList0_GetFirst(&o->used_bufs_list); + while (ln) { + NCDBuf *buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + buf->store = NULL; + ln = LinkedList0Node_Next(ln); + } + + ln = LinkedList0_GetFirst(&o->free_bufs_list); + while (ln) { + LinkedList0Node *next_ln = LinkedList0Node_Next(ln); + NCDBuf *buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + BFree(buf); + ln = next_ln; + } +} + +size_t NCDBufStore_BufSize (NCDBufStore *o) +{ + DebugObject_Access(&o->d_obj); + + return o->buf_size; +} + +NCDBuf * NCDBufStore_GetBuf (NCDBufStore *o) +{ + DebugObject_Access(&o->d_obj); + + NCDBuf *buf; + + LinkedList0Node *ln = LinkedList0_GetFirst(&o->free_bufs_list); + if (ln) { + buf = UPPER_OBJECT(ln, NCDBuf, list_node); + ASSERT(buf->store == o) + LinkedList0_Remove(&o->free_bufs_list, &buf->list_node); + } else { + bsize_t size = bsize_add(bsize_fromsize(sizeof(NCDBuf)), bsize_fromsize(o->buf_size)); + buf = BAllocSize(size); + if (!buf) { + return NULL; + } + buf->store = o; + } + + LinkedList0_Prepend(&o->used_bufs_list, &buf->list_node); + BRefTarget_Init(&buf->ref_target, ref_target_func_release); + + return buf; +} + +BRefTarget * NCDBuf_RefTarget (NCDBuf *o) +{ + return &o->ref_target; +} + +char * NCDBuf_Data (NCDBuf *o) +{ + return o->data; +} diff --git a/external/badvpn_dns/ncd/extra/NCDBuf.h b/external/badvpn_dns/ncd/extra/NCDBuf.h new file mode 100644 index 00000000..8542dcd5 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDBuf.h @@ -0,0 +1,61 @@ +/** + * @file NCDBuf.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_NCDBUF_H +#define NCD_NCDBUF_H + +#include + +#include +#include +#include + +typedef struct { + size_t buf_size; + LinkedList0 used_bufs_list; + LinkedList0 free_bufs_list; + DebugObject d_obj; +} NCDBufStore; + +typedef struct { + NCDBufStore *store; + LinkedList0Node list_node; + BRefTarget ref_target; + char data[]; +} NCDBuf; + +void NCDBufStore_Init (NCDBufStore *o, size_t buf_size); +void NCDBufStore_Free (NCDBufStore *o); +size_t NCDBufStore_BufSize (NCDBufStore *o); +NCDBuf * NCDBufStore_GetBuf (NCDBufStore *o); + +BRefTarget * NCDBuf_RefTarget (NCDBuf *o); +char * NCDBuf_Data (NCDBuf *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDIfConfig.c b/external/badvpn_dns/ncd/extra/NCDIfConfig.c new file mode 100644 index 00000000..2d89d5d3 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDIfConfig.c @@ -0,0 +1,483 @@ +/** + * @file NCDIfConfig.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "NCDIfConfig.h" + +#include + +#define IP_CMD "ip" +#define MODPROBE_CMD "modprobe" +#define RESOLVCONF_FILE "/etc/resolv.conf" +#define RESOLVCONF_TEMP_FILE "/etc/resolv.conf-ncd-temp" +#define TUN_DEVNODE "/dev/net/tun" + +static int run_command (const char *cmd) +{ + BLog(BLOG_INFO, "run: %s", cmd); + + return system(cmd); +} + +static int write_to_file (uint8_t *data, size_t data_len, FILE *f) +{ + while (data_len > 0) { + size_t bytes = fwrite(data, 1, data_len, f); + if (bytes == 0) { + return 0; + } + data += bytes; + data_len -= bytes; + } + + return 1; +} + +int NCDIfConfig_query (const char *ifname) +{ + struct ifreq ifr; + + int flags = 0; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (!s) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname); + if (ioctl(s, SIOCGIFFLAGS, &ifr)) { + BLog(BLOG_ERROR, "SIOCGIFFLAGS failed"); + goto fail1; + } + + flags |= NCDIFCONFIG_FLAG_EXISTS; + + if ((ifr.ifr_flags&IFF_UP)) { + flags |= NCDIFCONFIG_FLAG_UP; + + if ((ifr.ifr_flags&IFF_RUNNING)) { + flags |= NCDIFCONFIG_FLAG_RUNNING; + } + } + +fail1: + close(s); +fail0: + return flags; +} + +int NCDIfConfig_set_up (const char *ifname) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" link set %s up", ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_set_down (const char *ifname) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" link set %s down", ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *addr = (uint8_t *)&ifaddr.addr; + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr add %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_remove_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *addr = (uint8_t *)&ifaddr.addr; + + char cmd[50 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr del %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char addr_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(ifaddr.addr, addr_str); + + char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr add %s/%d dev %s", addr_str, ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_remove_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr) +{ + ASSERT(ifaddr.prefix >= 0) + ASSERT(ifaddr.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char addr_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(ifaddr.addr, addr_str); + + char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" addr del %s/%d dev %s", addr_str, ifaddr.prefix, ifname); + + return !run_command(cmd); +} + +static int route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *ifname) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 32) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + uint8_t *d_addr = (uint8_t *)&dest.addr; + + char gwstr[30]; + if (gateway) { + const uint8_t *g_addr = (uint8_t *)gateway; + sprintf(gwstr, " via %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, g_addr[0], g_addr[1], g_addr[2], g_addr[3]); + } else { + gwstr[0] = '\0'; + } + + char cmd[120 + IFNAMSIZ]; + sprintf(cmd, IP_CMD" route %s %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d%s metric %d dev %s", + cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, gwstr, metric, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device) +{ + return route_cmd("add", dest, gateway, metric, device); +} + +int NCDIfConfig_remove_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device) +{ + return route_cmd("del", dest, gateway, metric, device); +} + +static int route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *ifname) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 128) + + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return 0; + } + + char dest_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(dest.addr, dest_str); + + char gwstr[10 + IPADDR6_PRINT_MAX]; + if (gateway) { + strcpy(gwstr, " via "); + ipaddr6_print_addr(*gateway, gwstr + strlen(gwstr)); + } else { + gwstr[0] = '\0'; + } + + char cmd[70 + IPADDR6_PRINT_MAX + IPADDR6_PRINT_MAX + IFNAMSIZ]; + sprintf(cmd, IP_CMD" route %s %s/%d%s metric %d dev %s", + cmdtype, dest_str, dest.prefix, gwstr, metric, ifname); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device) +{ + return route_cmd6("add", dest, gateway, metric, device); +} + +int NCDIfConfig_remove_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device) +{ + return route_cmd6("del", dest, gateway, metric, device); +} + +static int blackhole_route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, int metric) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 32) + + uint8_t *d_addr = (uint8_t *)&dest.addr; + + char cmd[120]; + sprintf(cmd, IP_CMD" route %s blackhole %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d metric %d", + cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, metric); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric) +{ + return blackhole_route_cmd("add", dest, metric); +} + +int NCDIfConfig_remove_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric) +{ + return blackhole_route_cmd("del", dest, metric); +} + +static int blackhole_route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, int metric) +{ + ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del")) + ASSERT(dest.prefix >= 0) + ASSERT(dest.prefix <= 128) + + char dest_str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(dest.addr, dest_str); + + char cmd[70 + IPADDR6_PRINT_MAX]; + sprintf(cmd, IP_CMD" route %s blackhole %s/%d metric %d", + cmdtype, dest_str, dest.prefix, metric); + + return !run_command(cmd); +} + +int NCDIfConfig_add_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric) +{ + return blackhole_route_cmd6("add", dest, metric); +} + +int NCDIfConfig_remove_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric) +{ + return blackhole_route_cmd6("del", dest, metric); +} + +int NCDIfConfig_set_resolv_conf (const char *data, size_t data_len) +{ + FILE *temp_file = fopen(RESOLVCONF_TEMP_FILE, "w"); + if (!temp_file) { + BLog(BLOG_ERROR, "failed to open resolvconf temp file"); + goto fail0; + } + + char line[] = "# generated by badvpn-ncd\n"; + if (!write_to_file((uint8_t *)line, strlen(line), temp_file) || + !write_to_file((uint8_t *)data, data_len, temp_file) + ) { + BLog(BLOG_ERROR, "failed to write to resolvconf temp file"); + goto fail1; + } + + if (fclose(temp_file) != 0) { + BLog(BLOG_ERROR, "failed to close resolvconf temp file"); + return 0; + } + + if (rename(RESOLVCONF_TEMP_FILE, RESOLVCONF_FILE) < 0) { + BLog(BLOG_ERROR, "failed to rename resolvconf temp file to resolvconf file"); + return 0; + } + + return 1; + +fail1: + fclose(temp_file); +fail0: + return 0; +} + +static int open_tuntap (const char *ifname, int flags) +{ + if (strlen(ifname) >= IFNAMSIZ) { + BLog(BLOG_ERROR, "ifname too long"); + return -1; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + BLog(BLOG_ERROR, "open tun failed"); + return -1; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = flags; + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "TUNSETIFF failed"); + close(fd); + return -1; + } + + return fd; +} + +int NCDIfConfig_make_tuntap (const char *ifname, const char *owner, int tun) +{ + // load tun module if needed + if (access(TUN_DEVNODE, F_OK) < 0) { + if (run_command(MODPROBE_CMD" tun") != 0) { + BLog(BLOG_ERROR, "modprobe tun failed"); + } + } + + int fd; + if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) { + goto fail0; + } + + if (owner) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail1; + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(owner, &pwd, buf, bufsize, &res); + if (!res) { + BLog(BLOG_ERROR, "getpwnam_r failed"); + free(buf); + goto fail1; + } + + int uid = pwd.pw_uid; + + free(buf); + + if (ioctl(fd, TUNSETOWNER, uid) < 0) { + BLog(BLOG_ERROR, "TUNSETOWNER failed"); + goto fail1; + } + } + + if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) { + BLog(BLOG_ERROR, "TUNSETPERSIST failed"); + goto fail1; + } + + close(fd); + + return 1; + +fail1: + close(fd); +fail0: + return 0; +} + +int NCDIfConfig_remove_tuntap (const char *ifname, int tun) +{ + int fd; + if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) { + goto fail0; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) { + BLog(BLOG_ERROR, "TUNSETPERSIST failed"); + goto fail1; + } + + close(fd); + + return 1; + +fail1: + close(fd); +fail0: + return 0; +} diff --git a/external/badvpn_dns/ncd/extra/NCDIfConfig.h b/external/badvpn_dns/ncd/extra/NCDIfConfig.h new file mode 100644 index 00000000..711979fe --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDIfConfig.h @@ -0,0 +1,70 @@ +/** + * @file NCDIfConfig.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDIFCONFIG_H +#define BADVPN_NCD_NCDIFCONFIG_H + +#include + +#include +#include + +#define NCDIFCONFIG_FLAG_EXISTS (1 << 0) +#define NCDIFCONFIG_FLAG_UP (1 << 1) +#define NCDIFCONFIG_FLAG_RUNNING (1 << 2) + +int NCDIfConfig_query (const char *ifname); + +int NCDIfConfig_set_up (const char *ifname); +int NCDIfConfig_set_down (const char *ifname); + +int NCDIfConfig_add_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr); +int NCDIfConfig_remove_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr); + +int NCDIfConfig_add_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr); +int NCDIfConfig_remove_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr); + +int NCDIfConfig_add_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device); +int NCDIfConfig_remove_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device); + +int NCDIfConfig_add_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device); +int NCDIfConfig_remove_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device); + +int NCDIfConfig_add_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric); +int NCDIfConfig_remove_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric); + +int NCDIfConfig_add_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric); +int NCDIfConfig_remove_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric); + +int NCDIfConfig_set_resolv_conf (const char *data, size_t data_len); + +int NCDIfConfig_make_tuntap (const char *ifname, const char *owner, int tun); +int NCDIfConfig_remove_tuntap (const char *ifname, int tun); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.c b/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.c new file mode 100644 index 00000000..8b457ef4 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.c @@ -0,0 +1,446 @@ +/** + * @file NCDInterfaceMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) + +static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type); +static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len); +static void report_error (NCDInterfaceMonitor *o); +static int send_next_dump_request (NCDInterfaceMonitor *o); +static void netlink_fd_handler (NCDInterfaceMonitor *o, int events); +static void process_buffer (NCDInterfaceMonitor *o); +static void more_job_handler (NCDInterfaceMonitor *o); + +static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = seq; + req.g.rtgen_family = family; + + int res = write(fd, &req, sizeof(req)); + if (res < 0) { + BLog(BLOG_ERROR, "write failed"); + return 0; + } + if (res != sizeof(req)) { + BLog(BLOG_ERROR, "write short"); + return 0; + } + + return 1; +} + +static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len) +{ + for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) { + uint8_t *attr = RTA_DATA(rta); + int attr_len = RTA_PAYLOAD(rta); + + if (rta->rta_type == type) { + *out_attr = attr; + *out_attr_len = attr_len; + return 1; + } + } + + return 0; +} + +static void report_error (NCDInterfaceMonitor *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +static int send_next_dump_request (NCDInterfaceMonitor *o) +{ + ASSERT(o->dump_queue) + + if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK); + } + else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR); + } + else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) { + o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR; + return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR); + } + + ASSERT(0) + return 0; +} + +void netlink_fd_handler (NCDInterfaceMonitor *o, int events) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_bfd) + + // handler fd error + if (o->buf_left >= 0) { + BLog(BLOG_ERROR, "file descriptor error"); + goto fail; + } + + // read from netlink fd + int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf)); + if (len < 0) { + BLog(BLOG_ERROR, "read failed"); + goto fail; + } + + // stop receiving fd events + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0); + + // set buffer + o->buf_nh = &o->buf.nlh; + o->buf_left = len; + + // process buffer + process_buffer(o); + return; + +fail: + report_error(o); +} + +void process_buffer (NCDInterfaceMonitor *o) +{ + ASSERT(o->buf_left >= 0) + ASSERT(o->have_bfd) + + int done = 0; + + for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) { + if (o->buf_nh->nlmsg_type == NLMSG_DONE) { + done = 1; + break; + } + + struct nlmsghdr *buf = o->buf_nh; + void *pl = NLMSG_DATA(buf); + int pl_len = NLMSG_PAYLOAD(buf, 0); + + struct NCDInterfaceMonitor_event ev; + + switch (buf->nlmsg_type) { + case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean... + if (pl_len < sizeof(struct ifinfomsg)) { + BLog(BLOG_ERROR, "ifinfomsg too short"); + goto fail; + } + struct ifinfomsg *msg = pl; + + if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) { + ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN; + goto dispatch; + } + } break; + + case RTM_NEWADDR: + case RTM_DELADDR: { + if (pl_len < sizeof(struct ifaddrmsg)) { + BLog(BLOG_ERROR, "ifaddrmsg too short"); + goto fail; + } + struct ifaddrmsg *msg = pl; + + void *addr; + int addr_len; + if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) { + break; + } + + if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) { + if (addr_len != 4 || msg->ifa_prefixlen > 32) { + BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg"); + goto fail; + } + + ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED; + ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr; + ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen; + goto dispatch; + } + + if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) { + if (addr_len != 16 || msg->ifa_prefixlen > 128) { + BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg"); + goto fail; + } + + ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED; + memcpy(ev.u.ipv6_addr.addr.addr.bytes, ((struct in6_addr *)addr)->s6_addr, 16); + ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen; + ev.u.ipv6_addr.addr_flags = 0; + ev.u.ipv6_addr.scope = msg->ifa_scope; + if (!(msg->ifa_flags & IFA_F_PERMANENT)) { + ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC; + } + goto dispatch; + } + } break; + } + + continue; + + dispatch: + // move to next message + o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left); + + // schedule more job + BPending_Set(&o->more_job); + + // dispatch event + o->handler(o->user, ev); + return; + } + + if (done) { + if (o->dump_queue) { + // increment dump request sequence number + o->dump_seq++; + + // send next dump request + if (!send_next_dump_request(o)) { + goto fail; + } + } + else if (o->event_netlink_fd >= 0) { + // stop watching dump fd + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + o->have_bfd = 0; + + // close dump fd, make event fd current + close(o->netlink_fd); + o->netlink_fd = o->event_netlink_fd; + o->event_netlink_fd = -1; + + // start watching event fd + BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail; + } + o->have_bfd = 1; + } + } + + // set no buffer + o->buf_left = -1; + + // continue receiving fd events + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + return; + +fail: + report_error(o); +} + +void more_job_handler (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buf_left >= 0) + + // process buffer + process_buffer(o); + return; +} + +int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user, + NCDInterfaceMonitor_handler handler, + NCDInterfaceMonitor_handler_error handler_error) +{ + ASSERT(watch_events) + ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0) + ASSERT(handler) + ASSERT(handler_error) + BNetwork_Assert(); + + // init arguments + o->ifindex = ifindex; + o->watch_events = watch_events; + o->reactor = reactor; + o->user = user; + o->handler = handler; + o->handler_error = handler_error; + + // init dump netlink fd + if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + if (!badvpn_set_nonblocking(o->netlink_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init event netlink fd + if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + if (!badvpn_set_nonblocking(o->event_netlink_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // build bind address + struct sockaddr_nl sa; + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = 0; + if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK; + if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR; + if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR; + + // bind event netlink fd + if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail2; + } + + // set dump state + o->dump_queue = watch_events; + o->dump_seq = 0; + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o); + if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + o->have_bfd = 1; + + // set nothing in buffer + o->buf_left = -1; + + // init more job + BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o); + + // send first dump request + if (!send_next_dump_request(o)) { + goto fail3; + } + + // wait for reading fd + BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ); + + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + BPending_Free(&o->more_job); + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); +fail2: + close(o->event_netlink_fd); +fail1: + close(o->netlink_fd); +fail0: + return 0; +} + +void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free more job + BPending_Free(&o->more_job); + + // free BFileDescriptor + if (o->have_bfd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close event fd, in case we're still dumping + if (o->event_netlink_fd >= 0) { + close(o->event_netlink_fd); + } + + // close fd + close(o->netlink_fd); +} + +void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->have_bfd) + + if (o->buf_left >= 0) { + BPending_Unset(&o->more_job); + } else { + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0); + } +} + +void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->have_bfd) + + if (o->buf_left >= 0) { + BPending_Set(&o->more_job); + } else { + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + } +} diff --git a/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.h b/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.h new file mode 100644 index 00000000..805b8de9 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDInterfaceMonitor.h @@ -0,0 +1,160 @@ +/** + * @file NCDInterfaceMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDINTERFACEMONITOR_H +#define BADVPN_NCD_NCDINTERFACEMONITOR_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define NCDIFMONITOR_WATCH_LINK (1 << 0) +#define NCDIFMONITOR_WATCH_IPV4_ADDR (1 << 1) +#define NCDIFMONITOR_WATCH_IPV6_ADDR (1 << 2) + +#define NCDIFMONITOR_EVENT_LINK_UP 1 +#define NCDIFMONITOR_EVENT_LINK_DOWN 2 +#define NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED 3 +#define NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED 4 +#define NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED 5 +#define NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED 6 + +#define NCDIFMONITOR_ADDR_FLAG_DYNAMIC (1 << 0) + +struct NCDInterfaceMonitor_event { + int event; + union { + struct { + struct ipv4_ifaddr addr; + } ipv4_addr; + struct { + struct ipv6_ifaddr addr; + int addr_flags; + uint8_t scope; + } ipv6_addr; + } u; +}; + +/** + * Handler called to report an interface event. + * Note that the event reporter does not keep any interface state, and as such may + * report redundant events. You should therefore handle events in an idempotent + * fashion. + * + * @param event.event event type. One of: + * - NCDIFMONITOR_EVENT_LINK_UP, NCDIFMONITOR_EVENT_LINK_DOWN, + * - NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED, + * - NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED. + * Only events that correspont to enabled watch flags are reported. + * @param event.ipv4_addr.addr the IPv4 address and prefix length + * @param event.ipv6_addr.addr the IPv6 address, prefix length and scope + * @param event.ipv6_addr.addr_flags IPv6 address flags. Valid flags: + * - NCDIFMONITOR_ADDR_FLAG_DYNAMIC - this address was assigned dynamically (NDP) + */ +typedef void (*NCDInterfaceMonitor_handler) (void *user, struct NCDInterfaceMonitor_event event); + +/** + * Handler called when an error occurs. + * The event reporter must be freed from within the job context of this + * handler, and no other operations must be performed. + */ +typedef void (*NCDInterfaceMonitor_handler_error) (void *user); + +/** + * Watches for network interface events using a Linux rtnetlink socket. + */ +typedef struct { + int ifindex; + int watch_events; + BReactor *reactor; + void *user; + NCDInterfaceMonitor_handler handler; + NCDInterfaceMonitor_handler_error handler_error; + int netlink_fd; + int event_netlink_fd; + int dump_queue; + uint32_t dump_seq; + BFileDescriptor bfd; + int have_bfd; + union { + uint8_t buf[4096]; + struct nlmsghdr nlh; + } buf; + struct nlmsghdr *buf_nh; + int buf_left; + BPending more_job; + DebugError d_err; + DebugObject d_obj; +} NCDInterfaceMonitor; + +/** + * Initializes the event reporter. + * The reporter is not paused initially. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param ifindex index of network interface to report events for + * @param watch_events mask specifying what kind of events to report. Valid flags are + * NCDIFMONITOR_WATCH_LINK, NCDIFMONITOR_WATCH_IPV4_ADDR, NCDIFMONITOR_WATCH_IPV6_ADDR. + * At least one flag must be provided. + * @param reactor reactor we live in + * @param user argument to handlers + * @param handler handler to report interface events to + * @param handler_error error handler + * @return 1 on success, 0 on failure + */ +int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user, + NCDInterfaceMonitor_handler handler, + NCDInterfaceMonitor_handler_error handler_error) WARN_UNUSED; + +/** + * Frees the event reporter. + */ +void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o); + +/** + * Pauses event reporting. + * This operation is idempotent. + */ +void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o); + +/** + * Resumes event reporting. + * This operation is idempotent. + */ +void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDRequestClient.c b/external/badvpn_dns/ncd/extra/NCDRequestClient.c new file mode 100644 index 00000000..edf6378f --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDRequestClient.c @@ -0,0 +1,647 @@ +/** + * @file NCDRequestClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "NCDRequestClient.h" + +#include + +#define SEND_PAYLOAD_MTU 32768 +#define RECV_PAYLOAD_MTU 32768 + +#define SEND_MTU (SEND_PAYLOAD_MTU + sizeof(struct requestproto_header)) +#define RECV_MTU (RECV_PAYLOAD_MTU + sizeof(struct requestproto_header)) + +#define CSTATE_CONNECTING 1 +#define CSTATE_CONNECTED 2 + +#define RSTATE_SENDING_REQUEST 1 +#define RSTATE_READY 2 +#define RSTATE_SENDING_REQUEST_ABORT 3 +#define RSTATE_SENDING_ABORT 4 +#define RSTATE_WAITING_END 5 +#define RSTATE_DEAD_SENDING 6 + +static int uint32_comparator (void *unused, void *vv1, void *vv2); +static void report_error (NCDRequestClient *o); +static void request_report_finished (NCDRequestClientRequest *o, int is_error); +static void connector_handler (NCDRequestClient *o, int is_error); +static void connection_handler (NCDRequestClient *o, int event); +static void decoder_handler_error (NCDRequestClient *o); +static void recv_if_handler_send (NCDRequestClient *o, uint8_t *data, int data_len); +static struct NCDRequestClient_req * find_req (NCDRequestClient *o, uint32_t request_id); +static int get_free_request_id (NCDRequestClient *o, uint32_t *out); +static int build_requestproto_packet (uint32_t request_id, uint32_t type, NCDValRef payload_value, uint8_t **out_data, int *out_len); +static void build_nodata_packet (uint32_t request_id, uint32_t type, uint8_t *data, int *out_len); +static int req_is_aborted (struct NCDRequestClient_req *req); +static void req_abort (struct NCDRequestClient_req *req); +static void req_free (struct NCDRequestClient_req *req); +static void req_send_abort (struct NCDRequestClient_req *req); +static void req_qflow_send_iface_handler_done (struct NCDRequestClient_req *req); + +static int uint32_comparator (void *unused, void *vv1, void *vv2) +{ + uint32_t *v1 = vv1; + uint32_t *v2 = vv2; + return B_COMPARE(*v1, *v2); +} + +static void report_error (NCDRequestClient *o) +{ + ASSERT(!o->is_error) + + o->is_error = 1; + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +static void request_report_finished (NCDRequestClientRequest *o, int is_error) +{ + o->req = NULL; + + DEBUGERROR(&o->d_err, o->handler_finished(o->user, is_error)) +} + +static void connector_handler (NCDRequestClient *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTING) + + // check error + if (is_error) { + BLog(BLOG_ERROR, "failed to connect to socket"); + goto fail0; + } + + BPendingGroup *pg = BReactor_PendingGroup(o->reactor); + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->con); + BConnection_RecvAsync_Init(&o->con); + StreamPassInterface *con_send_if = BConnection_SendAsync_GetIf(&o->con); + StreamRecvInterface *con_recv_if = BConnection_RecvAsync_GetIf(&o->con); + + // init receive interface + PacketPassInterface_Init(&o->recv_if, RECV_MTU, (PacketPassInterface_handler_send)recv_if_handler_send, o, pg); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->recv_decoder, con_recv_if, &o->recv_if, pg, o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init send sender + PacketStreamSender_Init(&o->send_sender, con_send_if, PACKETPROTO_ENCLEN(SEND_MTU), pg); + + // init send queue + PacketPassFifoQueue_Init(&o->send_queue, PacketStreamSender_GetInput(&o->send_sender), pg); + + // set state connected + o->state = CSTATE_CONNECTED; + + // call connected handler + o->handler_connected(o->user); + return; + +fail1: + PacketPassInterface_Free(&o->recv_if); + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); +fail0: + report_error(o); +} + +static void connection_handler (NCDRequestClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + + BLog(BLOG_ERROR, "connection error"); + + report_error(o); +} + +static void decoder_handler_error (NCDRequestClient *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + + BLog(BLOG_ERROR, "decoder error"); + + report_error(o); +} + +static void recv_if_handler_send (NCDRequestClient *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->state == CSTATE_CONNECTED) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_MTU) + + // accept packet + PacketPassInterface_Done(&o->recv_if); + + if (data_len < sizeof(struct requestproto_header)) { + BLog(BLOG_ERROR, "missing requestproto header"); + goto fail; + } + + struct requestproto_header header; + memcpy(&header, data, sizeof(header)); + uint32_t request_id = ltoh32(header.request_id); + uint32_t type = ltoh32(header.type); + + uint8_t *payload = data + sizeof(header); + int payload_len = data_len - sizeof(header); + + // find request + struct NCDRequestClient_req *req = find_req(o, request_id); + if (!req) { + BLog(BLOG_ERROR, "received packet with unknown request ID"); + goto fail; + } + + switch (type) { + case REQUESTPROTO_TYPE_SERVER_REPLY: { + switch (o->state) { + case RSTATE_READY: { + // init memory + NCDValMem mem; + NCDValMem_Init(&mem); + + // parse payload + NCDValRef payload_value; + if (!NCDValParser_Parse((char *)payload, payload_len, &mem, &payload_value)) { + BLog(BLOG_ERROR, "failed to parse reply payload"); + NCDValMem_Free(&mem); + goto fail; + } + + // call reply handler + req->creq->handler_reply(req->creq->user, mem, payload_value); + return; + } break; + + case RSTATE_SENDING_ABORT: + case RSTATE_WAITING_END: + return; + + default: + BLog(BLOG_ERROR, "received unexpected reply"); + goto fail; + } + } break; + + case REQUESTPROTO_TYPE_SERVER_FINISHED: + case REQUESTPROTO_TYPE_SERVER_ERROR: { + if (payload_len != 0) { + BLog(BLOG_ERROR, "finshed/aborted message has non-empty payload"); + goto fail; + } + + NCDRequestClientRequest *creq = req->creq; + req->creq = NULL; + + switch (req->state) { + case RSTATE_SENDING_ABORT: { + // set state dying send + req->state = RSTATE_DEAD_SENDING; + } break; + + case RSTATE_WAITING_END: + case RSTATE_READY: { + // free req + req_free(req); + } break; + + default: + BLog(BLOG_ERROR, "received unexpected finished/aborted"); + goto fail; + } + + // report finished + if (creq) { + request_report_finished(creq, type == REQUESTPROTO_TYPE_SERVER_ERROR); + } + return; + } break; + + default: + BLog(BLOG_ERROR, "received invalid message type"); + goto fail; + } + + ASSERT(0) + +fail: + report_error(o); +} + +static struct NCDRequestClient_req * find_req (NCDRequestClient *o, uint32_t request_id) +{ + BAVLNode *tn = BAVL_LookupExact(&o->reqs_tree, &request_id); + if (!tn) { + return NULL; + } + + struct NCDRequestClient_req *req = UPPER_OBJECT(tn, struct NCDRequestClient_req, reqs_tree_node); + ASSERT(req->request_id == request_id) + + return req; +} + +static int get_free_request_id (NCDRequestClient *o, uint32_t *out) +{ + uint32_t first = o->next_request_id; + + do { + if (!find_req(o, o->next_request_id)) { + *out = o->next_request_id; + return 1; + } + o->next_request_id++; + } while (o->next_request_id != first); + + return 0; +} + +static int build_requestproto_packet (uint32_t request_id, uint32_t type, NCDValRef payload_value, uint8_t **out_data, int *out_len) +{ + ExpString str; + if (!ExpString_Init(&str)) { + BLog(BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + if (!ExpString_AppendZeros(&str, sizeof(struct packetproto_header) + sizeof(struct requestproto_header))) { + BLog(BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + + if (!NCDVal_IsInvalid(payload_value) && !NCDValGenerator_AppendGenerate(payload_value, &str)) { + BLog(BLOG_ERROR, "NCDValGenerator_AppendGenerate failed"); + goto fail1; + } + + size_t len = ExpString_Length(&str); + if (len > INT_MAX || len > PACKETPROTO_ENCLEN(SEND_MTU) || len - sizeof(struct packetproto_header) > UINT16_MAX) { + BLog(BLOG_ERROR, "reply is too long"); + goto fail1; + } + + uint8_t *packet = (uint8_t *)ExpString_Get(&str); + + struct packetproto_header pp; + pp.len = htol16(len - sizeof(struct packetproto_header)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + rp.type = htol32(type); + + memcpy(packet, &pp, sizeof(pp)); + memcpy(packet + sizeof(pp), &rp, sizeof(rp)); + + *out_data = packet; + *out_len = len; + return 1; + +fail1: + ExpString_Free(&str); +fail0: + return 0; +} + +static void build_nodata_packet (uint32_t request_id, uint32_t type, uint8_t *data, int *out_len) +{ + struct packetproto_header pp; + pp.len = htol16(sizeof(struct requestproto_header)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + rp.type = htol32(type); + + memcpy(data, &pp, sizeof(pp)); + memcpy(data + sizeof(pp), &rp, sizeof(rp)); + + *out_len = sizeof(pp) + sizeof(rp); +} + +static int req_is_aborted (struct NCDRequestClient_req *req) +{ + switch (req->state) { + case RSTATE_SENDING_REQUEST: + case RSTATE_READY: + return 0; + default: + return 1; + } +} + +static void req_abort (struct NCDRequestClient_req *req) +{ + ASSERT(!req_is_aborted(req)) + ASSERT(!req->client->is_error) + + switch (req->state) { + case RSTATE_SENDING_REQUEST: { + req->state = RSTATE_SENDING_REQUEST_ABORT; + } break; + + case RSTATE_READY: { + req_send_abort(req); + } break; + + default: ASSERT(0); + } +} + +static void req_free (struct NCDRequestClient_req *req) +{ + NCDRequestClient *client = req->client; + PacketPassFifoQueueFlow_AssertFree(&req->send_qflow); + ASSERT(!req->creq) + + // free queue flow + PacketPassFifoQueueFlow_Free(&req->send_qflow); + + // free request data + free(req->request_data); + + // remove from reqs tree + BAVL_Remove(&client->reqs_tree, &req->reqs_tree_node); + + // free structure + free(req); +} + +static void req_send_abort (struct NCDRequestClient_req *req) +{ + // build packet + build_nodata_packet(req->request_id, REQUESTPROTO_TYPE_CLIENT_ABORT, req->request_data, &req->request_len); + + // start sending + PacketPassInterface_Sender_Send(req->send_qflow_iface, req->request_data, req->request_len); + + // set state sending abort + req->state = RSTATE_SENDING_ABORT; +} + +static void req_qflow_send_iface_handler_done (struct NCDRequestClient_req *req) +{ + switch (req->state) { + case RSTATE_SENDING_REQUEST: { + // set state ready + req->state = RSTATE_READY; + + // call sent handler + req->creq->handler_sent(req->creq->user); + return; + } break; + + case RSTATE_SENDING_REQUEST_ABORT: { + // send abort + req_send_abort(req); + } break; + + case RSTATE_SENDING_ABORT: { + // set state waiting end + req->state = RSTATE_WAITING_END; + } break; + + case RSTATE_DEAD_SENDING: { + // free req + req_free(req); + } break; + + default: ASSERT(0); + } +} + +int NCDRequestClient_Init (NCDRequestClient *o, struct BConnection_addr addr, BReactor *reactor, void *user, + NCDRequestClient_handler_error handler_error, + NCDRequestClient_handler_connected handler_connected) +{ + ASSERT(handler_error) + ASSERT(handler_connected) + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler_error = handler_error; + o->handler_connected = handler_connected; + + // init connector + if (!BConnector_InitGeneric(&o->connector, addr, reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_InitGeneric failed"); + goto fail0; + } + + // init reqs tree + BAVL_Init(&o->reqs_tree, OFFSET_DIFF(struct NCDRequestClient_req, request_id, reqs_tree_node), uint32_comparator, NULL); + + // set next request ID + o->next_request_id = 0; + + // set state connecting + o->state = CSTATE_CONNECTING; + + // set is not error + o->is_error = 0; + + DebugCounter_Init(&o->d_reqests_ctr); + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void NCDRequestClient_Free (NCDRequestClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + DebugCounter_Free(&o->d_reqests_ctr); + + if (o->state == CSTATE_CONNECTED) { + // allow freeing queue flow + PacketPassFifoQueue_PrepareFree(&o->send_queue); + + // free remaining reqs + BAVLNode *tn; + while (tn = BAVL_GetFirst(&o->reqs_tree)) { + struct NCDRequestClient_req *req = UPPER_OBJECT(tn, struct NCDRequestClient_req, reqs_tree_node); + ASSERT(!req->creq) + req_free(req); + } + + // free connection stuff + PacketPassFifoQueue_Free(&o->send_queue); + PacketStreamSender_Free(&o->send_sender); + PacketProtoDecoder_Free(&o->recv_decoder); + PacketPassInterface_Free(&o->recv_if); + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); + } + + // free connector + BConnector_Free(&o->connector); +} + +int NCDRequestClientRequest_Init (NCDRequestClientRequest *o, NCDRequestClient *client, NCDValRef payload_value, void *user, + NCDRequestClientRequest_handler_sent handler_sent, + NCDRequestClientRequest_handler_reply handler_reply, + NCDRequestClientRequest_handler_finished handler_finished) +{ + ASSERT(client->state == CSTATE_CONNECTED) + DebugError_AssertNoError(&client->d_err); + ASSERT(!NCDVal_IsInvalid(payload_value)) + ASSERT(handler_sent) + ASSERT(handler_reply) + ASSERT(handler_finished) + + // init arguments + o->client = client; + o->user = user; + o->handler_sent = handler_sent; + o->handler_reply = handler_reply; + o->handler_finished = handler_finished; + + // allocate req structure + struct NCDRequestClient_req *req = malloc(sizeof(*req)); + if (!req) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // allocate request ID + if (!get_free_request_id(client, &req->request_id)) { + BLog(BLOG_ERROR, "failed to allocate request ID"); + goto fail1; + } + + // insert to reqs tree + int res = BAVL_Insert(&client->reqs_tree, &req->reqs_tree_node, NULL); + ASSERT_EXECUTE(res) + + // set pointers + o->req = req; + req->creq = o; + req->client = client; + + // build request + if (!build_requestproto_packet(req->request_id, REQUESTPROTO_TYPE_CLIENT_REQUEST, payload_value, &req->request_data, &req->request_len)) { + BLog(BLOG_ERROR, "failed to build request"); + goto fail2; + } + + // init queue flow + PacketPassFifoQueueFlow_Init(&req->send_qflow, &client->send_queue); + + // init send interface + req->send_qflow_iface = PacketPassFifoQueueFlow_GetInput(&req->send_qflow); + PacketPassInterface_Sender_Init(req->send_qflow_iface, (PacketPassInterface_handler_done)req_qflow_send_iface_handler_done, req); + + // start sending request + PacketPassInterface_Sender_Send(req->send_qflow_iface, req->request_data, req->request_len); + + // set state sending request + req->state = RSTATE_SENDING_REQUEST; + + DebugCounter_Increment(&client->d_reqests_ctr); + DebugError_Init(&o->d_err, BReactor_PendingGroup(client->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + BAVL_Remove(&client->reqs_tree, &req->reqs_tree_node); +fail1: + free(req); +fail0: + return 0; +} + +void NCDRequestClientRequest_Free (NCDRequestClientRequest *o) +{ + struct NCDRequestClient_req *req = o->req; + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + DebugCounter_Decrement(&o->client->d_reqests_ctr); + + if (req) { + ASSERT(req->creq == o) + + // remove reference to us + req->creq = NULL; + + // abort req if not already + if (!req->client->is_error && !req_is_aborted(req)) { + req_abort(req); + } + } +} + +void NCDRequestClientRequest_Abort (NCDRequestClientRequest *o) +{ + struct NCDRequestClient_req *req = o->req; + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + DebugError_AssertNoError(&o->client->d_err); + ASSERT(req) + ASSERT(req->creq == o) + ASSERT(!req_is_aborted(req)) + + // abort req + req_abort(req); +} diff --git a/external/badvpn_dns/ncd/extra/NCDRequestClient.h b/external/badvpn_dns/ncd/extra/NCDRequestClient.h new file mode 100644 index 00000000..7e0a6d5e --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDRequestClient.h @@ -0,0 +1,111 @@ +/** + * @file NCDRequestClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCDREQUESTCLIENT_H +#define BADVPN_NCDREQUESTCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct NCDRequestClient_req; + +typedef void (*NCDRequestClient_handler_error) (void *user); +typedef void (*NCDRequestClient_handler_connected) (void *user); +typedef void (*NCDRequestClientRequest_handler_sent) (void *user); +typedef void (*NCDRequestClientRequest_handler_reply) (void *user, NCDValMem reply_mem, NCDValRef reply_value); +typedef void (*NCDRequestClientRequest_handler_finished) (void *user, int is_error); + +typedef struct { + BReactor *reactor; + void *user; + NCDRequestClient_handler_error handler_error; + NCDRequestClient_handler_connected handler_connected; + BConnector connector; + BConnection con; + PacketPassFifoQueue send_queue; + PacketStreamSender send_sender; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + BAVL reqs_tree; + uint32_t next_request_id; + int state; + int is_error; + DebugCounter d_reqests_ctr; + DebugError d_err; + DebugObject d_obj; +} NCDRequestClient; + +typedef struct { + NCDRequestClient *client; + void *user; + NCDRequestClientRequest_handler_sent handler_sent; + NCDRequestClientRequest_handler_reply handler_reply; + NCDRequestClientRequest_handler_finished handler_finished; + struct NCDRequestClient_req *req; + DebugError d_err; + DebugObject d_obj; +} NCDRequestClientRequest; + +struct NCDRequestClient_req { + NCDRequestClientRequest *creq; + NCDRequestClient *client; + BAVLNode reqs_tree_node; + uint32_t request_id; + uint8_t *request_data; + int request_len; + PacketPassInterface *send_qflow_iface; + PacketPassFifoQueueFlow send_qflow; + int state; +}; + +int NCDRequestClient_Init (NCDRequestClient *o, struct BConnection_addr addr, BReactor *reactor, void *user, + NCDRequestClient_handler_error handler_error, + NCDRequestClient_handler_connected handler_connected) WARN_UNUSED; +void NCDRequestClient_Free (NCDRequestClient *o); + +int NCDRequestClientRequest_Init (NCDRequestClientRequest *o, NCDRequestClient *client, NCDValRef payload_value, void *user, + NCDRequestClientRequest_handler_sent handler_sent, + NCDRequestClientRequest_handler_reply handler_reply, + NCDRequestClientRequest_handler_finished handler_finished) WARN_UNUSED; +void NCDRequestClientRequest_Free (NCDRequestClientRequest *o); +void NCDRequestClientRequest_Abort (NCDRequestClientRequest *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.c b/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.c new file mode 100644 index 00000000..cec2a3d5 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.c @@ -0,0 +1,117 @@ +/** + * @file NCDRfkillMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "NCDRfkillMonitor.h" + +#include + +#define RFKILL_DEVICE_NODE "/dev/rfkill" + +static void rfkill_fd_handler (NCDRfkillMonitor *o, int events); + +void rfkill_fd_handler (NCDRfkillMonitor *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // read from netlink fd + struct rfkill_event event; + int len = read(o->rfkill_fd, &event, sizeof(event)); + if (len < 0) { + BLog(BLOG_ERROR, "read failed"); + return; + } + if (len != sizeof(event)) { + BLog(BLOG_ERROR, "read returned wrong length"); + return; + } + + // call handler + o->handler(o->user, event); + return; +} + +int NCDRfkillMonitor_Init (NCDRfkillMonitor *o, BReactor *reactor, NCDRfkillMonitor_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + + // open rfkill + if ((o->rfkill_fd = open(RFKILL_DEVICE_NODE, O_RDONLY)) < 0) { + BLog(BLOG_ERROR, "open failed"); + goto fail0; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->rfkill_fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->rfkill_fd, (BFileDescriptor_handler)rfkill_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->rfkill_fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void NCDRfkillMonitor_Free (NCDRfkillMonitor *o) +{ + DebugObject_Free(&o->d_obj); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // close rfkill + if (close(o->rfkill_fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} diff --git a/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.h b/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.h new file mode 100644 index 00000000..00aa2657 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/NCDRfkillMonitor.h @@ -0,0 +1,53 @@ +/** + * @file NCDRfkillMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_NCDRFKILLMONITOR_H +#define BADVPN_NCD_NCDRFKILLMONITOR_H + +#include + +#include +#include +#include + +typedef void (*NCDRfkillMonitor_handler) (void *user, struct rfkill_event event); + +typedef struct { + BReactor *reactor; + NCDRfkillMonitor_handler handler; + void *user; + int rfkill_fd; + BFileDescriptor bfd; + DebugObject d_obj; +} NCDRfkillMonitor; + +int NCDRfkillMonitor_Init (NCDRfkillMonitor *o, BReactor *reactor, NCDRfkillMonitor_handler handler, void *user) WARN_UNUSED; +void NCDRfkillMonitor_Free (NCDRfkillMonitor *o); + +#endif diff --git a/external/badvpn_dns/ncd/extra/address_utils.h b/external/badvpn_dns/ncd/extra/address_utils.h new file mode 100644 index 00000000..24ff79a4 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/address_utils.h @@ -0,0 +1,280 @@ +/** + * @file address_utils.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_ADDRESS_UTILS_H +#define NCD_ADDRESS_UTILS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ncd_read_baddr (NCDValRef val, BAddr *out) WARN_UNUSED; +static NCDValRef ncd_make_baddr (BAddr addr, NCDValMem *mem); +static int ncd_read_bconnection_addr (NCDValRef val, struct BConnection_addr *out_addr) WARN_UNUSED; + +static int ncd_read_baddr (NCDValRef val, BAddr *out) +{ + ASSERT(!NCDVal_IsInvalid(val)) + ASSERT(NCDVal_HasOnlyContinuousStrings(val)) + ASSERT(out) + + if (!NCDVal_IsList(val)) { + goto fail; + } + + NCDValRef type_val; + if (!NCDVal_ListReadHead(val, 1, &type_val)) { + goto fail; + } + if (!NCDVal_IsString(type_val)) { + goto fail; + } + + BAddr addr; + + if (NCDVal_StringEquals(type_val, "none")) { + if (!NCDVal_ListRead(val, 1, &type_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_NONE; + } + else if (NCDVal_StringEquals(type_val, "ipv4")) { + NCDValRef ipaddr_val; + NCDValRef port_val; + if (!NCDVal_ListRead(val, 3, &type_val, &ipaddr_val, &port_val)) { + goto fail; + } + if (!NCDVal_IsString(ipaddr_val) || !NCDVal_IsString(port_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_IPV4; + + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(ipaddr_val), NCDVal_StringLength(ipaddr_val), &addr.ipv4.ip)) { + goto fail; + } + + uintmax_t port; + if (!ncd_read_uintmax(port_val, &port) || port > UINT16_MAX) { + goto fail; + } + addr.ipv4.port = hton16(port); + } + else if (NCDVal_StringEquals(type_val, "ipv6")) { + NCDValRef ipaddr_val; + NCDValRef port_val; + if (!NCDVal_ListRead(val, 3, &type_val, &ipaddr_val, &port_val)) { + goto fail; + } + if (!NCDVal_IsString(ipaddr_val) || !NCDVal_IsString(port_val)) { + goto fail; + } + + addr.type = BADDR_TYPE_IPV6; + + struct ipv6_addr i6addr; + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(ipaddr_val), NCDVal_StringLength(ipaddr_val), &i6addr)) { + goto fail; + } + memcpy(addr.ipv6.ip, i6addr.bytes, 16); + + uintmax_t port; + if (!ncd_read_uintmax(port_val, &port) || port > UINT16_MAX) { + goto fail; + } + addr.ipv6.port = hton16(port); + } + else { + goto fail; + } + + *out = addr; + return 1; + +fail: + return 0; +} + +static NCDValRef ncd_make_baddr (BAddr addr, NCDValMem *mem) +{ + BAddr_Assert(&addr); + ASSERT(mem) + + NCDValRef val; + + switch (addr.type) { + default: + case BADDR_TYPE_NONE: { + val = NCDVal_NewList(mem, 1); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + const char *str = (addr.type == BADDR_TYPE_NONE ? "none" : "unknown"); + NCDValRef type_val = NCDVal_NewString(mem, str); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + } break; + + case BADDR_TYPE_IPV4: { + val = NCDVal_NewList(mem, 3); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + NCDValRef type_val = NCDVal_NewString(mem, "ipv4"); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + char ipaddr_buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr.ipv4.ip, ipaddr_buf); + NCDValRef ipaddr_val = NCDVal_NewString(mem, ipaddr_buf); + if (NCDVal_IsInvalid(ipaddr_val)) { + goto fail; + } + + NCDValRef port_val = ncd_make_uintmax(mem, ntoh16(addr.ipv4.port)); + if (NCDVal_IsInvalid(port_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, ipaddr_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, port_val)) { + goto fail; + } + } break; + + case BADDR_TYPE_IPV6: { + val = NCDVal_NewList(mem, 3); + if (NCDVal_IsInvalid(val)) { + goto fail; + } + + NCDValRef type_val = NCDVal_NewString(mem, "ipv6"); + if (NCDVal_IsInvalid(type_val)) { + goto fail; + } + + char ipaddr_buf[IPADDR6_PRINT_MAX]; + struct ipv6_addr i6addr; + memcpy(i6addr.bytes, addr.ipv6.ip, 16); + ipaddr6_print_addr(i6addr, ipaddr_buf); + NCDValRef ipaddr_val = NCDVal_NewString(mem, ipaddr_buf); + if (NCDVal_IsInvalid(ipaddr_val)) { + goto fail; + } + + NCDValRef port_val = ncd_make_uintmax(mem, ntoh16(addr.ipv6.port)); + if (NCDVal_IsInvalid(port_val)) { + goto fail; + } + + if (!NCDVal_ListAppend(val, type_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, ipaddr_val)) { + goto fail; + } + if (!NCDVal_ListAppend(val, port_val)) { + goto fail; + } + } break; + } + + return val; + +fail: + return NCDVal_NewInvalid(); +} + +static int ncd_read_bconnection_addr (NCDValRef val, struct BConnection_addr *out_addr) +{ + ASSERT(!NCDVal_IsInvalid(val)) + ASSERT(NCDVal_HasOnlyContinuousStrings(val)) + + if (!NCDVal_IsList(val)) { + goto fail; + } + + NCDValRef protocol_arg; + NCDValRef data_arg; + if (!NCDVal_ListRead(val, 2, &protocol_arg, &data_arg)) { + goto fail; + } + + if (!NCDVal_IsString(protocol_arg)) { + goto fail; + } + + if (NCDVal_StringEquals(protocol_arg, "unix")) { + if (!NCDVal_IsStringNoNulls(data_arg)) { + goto fail; + } + + *out_addr = BConnection_addr_unix(NCDVal_StringData(data_arg), NCDVal_StringLength(data_arg)); + } + else if (NCDVal_StringEquals(protocol_arg, "tcp")) { + BAddr baddr; + if (!ncd_read_baddr(data_arg, &baddr)) { + goto fail; + } + + *out_addr = BConnection_addr_baddr(baddr); + } + else { + goto fail; + } + + return 1; + +fail: + return 0; +} + +#endif diff --git a/external/badvpn_dns/ncd/extra/build_cmdline.c b/external/badvpn_dns/ncd/extra/build_cmdline.c new file mode 100644 index 00000000..ea96b0fa --- /dev/null +++ b/external/badvpn_dns/ncd/extra/build_cmdline.c @@ -0,0 +1,111 @@ +/** + * @file build_cmdline.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "build_cmdline.h" + +int ncd_build_cmdline (NCDModuleInst *i, int log_channel, NCDValRef cmd_arg, char **out_exec, CmdLine *out_cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + ASSERT(out_exec) + ASSERT(out_cl) + + if (!NCDVal_IsList(cmd_arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail0; + } + char *exec = ncd_strdup(exec_arg); + if (!exec) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + CmdLine cl; + if (!CmdLine_Init(&cl)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(&cl, exec)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(&cl, cstr, 0, cstr.length)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(&cl)) { + NCDModuleInst_Backend_Log(i, log_channel, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + *out_exec = exec; + *out_cl = cl; + return 1; + +fail2: + CmdLine_Free(&cl); +fail1: + free(exec); +fail0: + return 0; +} diff --git a/external/badvpn_dns/ncd/extra/build_cmdline.h b/external/badvpn_dns/ncd/extra/build_cmdline.h new file mode 100644 index 00000000..27abf18c --- /dev/null +++ b/external/badvpn_dns/ncd/extra/build_cmdline.h @@ -0,0 +1,38 @@ +/** + * @file build_cmdline.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_BUILD_CMDLINE_H +#define NCD_BUILD_CMDLINE_H + +#include +#include + +int ncd_build_cmdline (NCDModuleInst *i, int log_channel, NCDValRef cmd_arg, char **exec, CmdLine *cl); + +#endif diff --git a/external/badvpn_dns/ncd/extra/make_fast_names.h b/external/badvpn_dns/ncd/extra/make_fast_names.h new file mode 100644 index 00000000..3b7d72ac --- /dev/null +++ b/external/badvpn_dns/ncd/extra/make_fast_names.h @@ -0,0 +1,154 @@ +/** + * @file make_fast_names.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +// Input parameters: +// #define NAMES_PARAM_NAME +// #define NAMES_PARAM_TYPE struct instance +// #define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +// #define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +// #define NAMES_PARAM_MEMBER_NUM_NAMES num_names +// #define NAMES_PARAM_NUM_STATIC_NAMES 10 + +#define MakeFastNames_count_names MERGE(NAMES_PARAM_NAME, _count_names) +#define MakeFastNames_add_name MERGE(NAMES_PARAM_NAME, _add_name) +#define MakeFastNames_InitNames MERGE(NAMES_PARAM_NAME, _InitNames) +#define MakeFastNames_FreeNames MERGE(NAMES_PARAM_NAME, _FreeNames) +#define MakeFastNames_GetNames MERGE(NAMES_PARAM_NAME, _GetNames) + +static size_t MakeFastNames_count_names (const char *str, size_t str_len) +{ + size_t count = 1; + + while (str_len > 0) { + if (*str == '.') { + count++; + } + str++; + str_len--; + } + + return count; +} + +static int MakeFastNames_add_name (NAMES_PARAM_TYPE *o, NCDStringIndex *string_index, const char *str, size_t str_len, const char *remain, size_t remain_len) +{ + ASSERT(str) + ASSERT(!!o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES == (o->NAMES_PARAM_MEMBER_NUM_NAMES > NAMES_PARAM_NUM_STATIC_NAMES)) + + NCD_string_id_t id = NCDStringIndex_GetBin(string_index, str, str_len); + if (id < 0) { + return 0; + } + + if (o->NAMES_PARAM_MEMBER_NUM_NAMES < NAMES_PARAM_NUM_STATIC_NAMES) { + o->NAMES_PARAM_MEMBER_STATIC_NAMES[o->NAMES_PARAM_MEMBER_NUM_NAMES++] = id; + return 1; + } + + if (o->NAMES_PARAM_MEMBER_NUM_NAMES == NAMES_PARAM_NUM_STATIC_NAMES) { + size_t num_more = (!remain ? 0 : MakeFastNames_count_names(remain, remain_len)); + size_t num_all = o->NAMES_PARAM_MEMBER_NUM_NAMES + 1 + num_more; + + if (!(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES = BAllocArray(num_all, sizeof(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[0])))) { + return 0; + } + + memcpy(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES, o->NAMES_PARAM_MEMBER_STATIC_NAMES, NAMES_PARAM_NUM_STATIC_NAMES * sizeof(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[0])); + } + + o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES[o->NAMES_PARAM_MEMBER_NUM_NAMES++] = id; + + return 1; +} + +static int MakeFastNames_InitNames (NAMES_PARAM_TYPE *o, NCDStringIndex *string_index, const char *str, size_t str_len) +{ + ASSERT(str) + + o->NAMES_PARAM_MEMBER_NUM_NAMES = 0; + o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES = NULL; + + size_t i = 0; + while (i < str_len) { + if (str[i] == '.') { + if (!MakeFastNames_add_name(o, string_index, str, i, str + (i + 1), str_len - (i + 1))) { + goto fail; + } + str += i + 1; + str_len -= i + 1; + i = 0; + continue; + } + i++; + } + + if (!MakeFastNames_add_name(o, string_index, str, i, NULL, 0)) { + goto fail; + } + + return 1; + +fail: + BFree(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES); + return 0; +} + +static void MakeFastNames_FreeNames (NAMES_PARAM_TYPE *o) +{ + if (o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES) { + BFree(o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES); + } +} + +static NCD_string_id_t * MakeFastNames_GetNames (NAMES_PARAM_TYPE *o) +{ + ASSERT(o->NAMES_PARAM_MEMBER_NUM_NAMES > 0) + + return (o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES ? o->NAMES_PARAM_MEMBER_DYNAMIC_NAMES : o->NAMES_PARAM_MEMBER_STATIC_NAMES); +} + +#undef MakeFastNames_count_names +#undef MakeFastNames_add_name +#undef MakeFastNames_InitNames +#undef MakeFastNames_FreeNames +#undef MakeFastNames_GetNames + +#undef NAMES_PARAM_NAME +#undef NAMES_PARAM_TYPE +#undef NAMES_PARAM_MEMBER_DYNAMIC_NAMES +#undef NAMES_PARAM_MEMBER_STATIC_NAMES +#undef NAMES_PARAM_MEMBER_NUM_NAMES +#undef NAMES_PARAM_NUM_STATIC_NAMES diff --git a/external/badvpn_dns/ncd/extra/value_utils.h b/external/badvpn_dns/ncd/extra/value_utils.h new file mode 100644 index 00000000..2d011488 --- /dev/null +++ b/external/badvpn_dns/ncd/extra/value_utils.h @@ -0,0 +1,174 @@ +/** + * @file value_utils.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NCD_VALUE_UTILS_H +#define NCD_VALUE_UTILS_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ncd_is_none (NCDValRef val); +static NCDValRef ncd_make_boolean (NCDValMem *mem, int value, NCDStringIndex *string_index); +static int ncd_read_boolean (NCDValRef val); +static int ncd_read_uintmax (NCDValRef string, uintmax_t *out) WARN_UNUSED; +static int ncd_read_time (NCDValRef string, btime_t *out) WARN_UNUSED; +static NCD_string_id_t ncd_get_string_id (NCDValRef string, NCDStringIndex *string_index); +static NCDValRef ncd_make_uintmax (NCDValMem *mem, uintmax_t value); +static char * ncd_strdup (NCDValRef stringnonulls); + +static int ncd_is_none (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string) == NCD_STRING_NONE; + } else { + return NCDVal_StringEquals(string, ""); + } +} + +static NCDValRef ncd_make_boolean (NCDValMem *mem, int value, NCDStringIndex *string_index) +{ + ASSERT(mem) + ASSERT(string_index) + + NCD_string_id_t str_id = (value ? NCD_STRING_TRUE : NCD_STRING_FALSE); + return NCDVal_NewIdString(mem, str_id, string_index); +} + +static int ncd_read_boolean (NCDValRef string) +{ + ASSERT(NCDVal_IsString(string)) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string) == NCD_STRING_TRUE; + } else { + return NCDVal_StringEquals(string, "true"); + } +} + +static int ncd_read_uintmax (NCDValRef string, uintmax_t *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + size_t length = NCDVal_StringLength(string); + + if (NCDVal_IsContinuousString(string)) { + return parse_unsigned_integer_bin(NCDVal_StringData(string), length, out); + } + + b_cstring cstr = NCDVal_StringCstring(string); + + return parse_unsigned_integer_cstr(cstr, 0, cstr.length, out); +} + +static int ncd_read_time (NCDValRef string, btime_t *out) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(out) + + uintmax_t n; + if (!ncd_read_uintmax(string, &n)) { + return 0; + } + + if (n > INT64_MAX) { + return 0; + } + + *out = n; + return 1; +} + +static NCD_string_id_t ncd_get_string_id (NCDValRef string, NCDStringIndex *string_index) +{ + ASSERT(NCDVal_IsString(string)) + ASSERT(string_index) + + if (NCDVal_IsIdString(string)) { + return NCDVal_IdStringId(string); + } else if (NCDVal_IsContinuousString(string)) { + return NCDStringIndex_GetBin(string_index, NCDVal_StringData(string), NCDVal_StringLength(string)); + } + + b_cstring cstr = NCDVal_StringCstring(string); + + char *temp = b_cstring_strdup(cstr, 0, cstr.length); + if (!temp) { + return -1; + } + + NCD_string_id_t res = NCDStringIndex_GetBin(string_index, temp, cstr.length); + BFree(temp); + + return res; +} + +static NCDValRef ncd_make_uintmax (NCDValMem *mem, uintmax_t value) +{ + ASSERT(mem) + + int size = compute_decimal_repr_size(value); + + NCDValRef val = NCDVal_NewStringUninitialized(mem, size); + + if (!NCDVal_IsInvalid(val)) { + char *data = (char *)NCDVal_StringData(val); + generate_decimal_repr(value, data, size); + } + + return val; +} + +static char * ncd_strdup (NCDValRef stringnonulls) +{ + ASSERT(NCDVal_IsStringNoNulls(stringnonulls)) + + size_t length = NCDVal_StringLength(stringnonulls); + + if (NCDVal_IsContinuousString(stringnonulls)) { + return b_strdup_bin(NCDVal_StringData(stringnonulls), length); + } + + b_cstring cstr = NCDVal_StringCstring(stringnonulls); + + return b_cstring_strdup(cstr, 0, cstr.length); +} + +#endif diff --git a/external/badvpn_dns/ncd/include_linux_input.c b/external/badvpn_dns/ncd/include_linux_input.c new file mode 100644 index 00000000..8eb6c023 --- /dev/null +++ b/external/badvpn_dns/ncd/include_linux_input.c @@ -0,0 +1 @@ +#include diff --git a/external/badvpn_dns/ncd/make_name_indices.h b/external/badvpn_dns/ncd/make_name_indices.h new file mode 100644 index 00000000..b5c7c8e7 --- /dev/null +++ b/external/badvpn_dns/ncd/make_name_indices.h @@ -0,0 +1,104 @@ +/** + * @file make_name_indices.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_MAKE_NAME_INDICES_H +#define BADVPN_MAKE_NAME_INDICES_H + +#include +#include + +#include +#include +#include +#include + +static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names) WARN_UNUSED; + +static size_t split_string_inplace2__indices (char *str, char del) +{ + ASSERT(str) + + size_t num_extra_parts = 0; + + while (*str) { + if (*str == del) { + *str = '\0'; + num_extra_parts++; + } + str++; + } + + return num_extra_parts; +} + +static int ncd_make_name_indices (NCDStringIndex *string_index, const char *name, NCD_string_id_t **out_varnames, size_t *out_num_names) +{ + ASSERT(string_index) + ASSERT(name) + ASSERT(out_varnames) + ASSERT(out_num_names) + + char *data = b_strdup(name); + if (!data) { + goto fail0; + } + + size_t num_names = split_string_inplace2__indices(data, '.') + 1; + + NCD_string_id_t *varnames = BAllocArray(num_names, sizeof(varnames[0])); + if (!varnames) { + goto fail1; + } + + char *cur = data; + for (size_t i = 0; i < num_names; i++) { + NCD_string_id_t id = NCDStringIndex_Get(string_index, cur); + if (id < 0) { + goto fail2; + } + + varnames[i] = id; + cur += strlen(cur) + 1; + } + + free(data); + + *out_varnames = varnames; + *out_num_names = num_names; + return 1; + +fail2: + BFree(varnames); +fail1: + free(data); +fail0: + return 0; +} + +#endif diff --git a/external/badvpn_dns/ncd/modules/alias.c b/external/badvpn_dns/ncd/modules/alias.c new file mode 100644 index 00000000..f6bb7ecf --- /dev/null +++ b/external/badvpn_dns/ncd/modules/alias.c @@ -0,0 +1,148 @@ +/** + * @file alias.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * alias(string target) + * + * Variables and objects: + * - empty name - resolves target + * - nonempty name N - resolves target.N + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define NUM_STATIC_NAMES 4 + +struct instance { + NCDModuleInst *i; + NCD_string_id_t *dynamic_names; + size_t num_names; + NCD_string_id_t static_names[NUM_STATIC_NAMES]; +}; + +#define NAMES_PARAM_NAME AliasNames +#define NAMES_PARAM_TYPE struct instance +#define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +#define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +#define NAMES_PARAM_MEMBER_NUM_NAMES num_names +#define NAMES_PARAM_NUM_STATIC_NAMES NUM_STATIC_NAMES +#include + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef target_arg; + if (!NCDVal_ListRead(params->args, 1, &target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse name string + if (!AliasNames_InitNames(o, i->params->iparams->string_index, NCDVal_StringData(target_arg), NCDVal_StringLength(target_arg))) { + ModuleLog(i, BLOG_ERROR, "make_names failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + AliasNames_FreeNames(o); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = vo; + ASSERT(o->num_names > 0) + + NCD_string_id_t *names = AliasNames_GetNames(o); + + NCDObject object; + if (!NCDModuleInst_Backend_GetObj(o->i, names[0], &object)) { + return 0; + } + + NCDObject obj2; + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, o->num_names - 1, &obj2)) { + return 0; + } + + if (name == NCD_STRING_EMPTY) { + *out_object = obj2; + return 1; + } + + return NCDObject_GetObj(&obj2, name, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "alias", + .func_new2 = func_new, + .func_die = func_die, + .func_getobj = func_getobj, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_alias = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/arithmetic.c b/external/badvpn_dns/ncd/modules/arithmetic.c new file mode 100644 index 00000000..288a6372 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/arithmetic.c @@ -0,0 +1,404 @@ +/** + * @file arithmetic.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Arithmetic functions for unsigned integers. + * + * Synopsis: + * num_lesser(string n1, string n2) + * num_greater(string n1, string n2) + * num_lesser_equal(string n1, string n2) + * num_greater_equal(string n1, string n2) + * num_equal(string n1, string n2) + * num_different(string n1, string n2) + * + * Variables: + * (empty) - "true" or "false", reflecting the value of the relation in question + * + * Description: + * These statements perform arithmetic comparisons. The operands passed must be + * non-negative decimal integers representable in a uintmax_t. Otherwise, an error + * is triggered. + * + * Synopsis: + * num_add(string n1, string n2) + * num_subtract(string n1, string n2) + * num_multiply(string n1, string n2) + * num_divide(string n1, string n2) + * num_modulo(string n1, string n2) + * + * Description: + * These statements perform arithmetic operations. The operands passed must be + * non-negative decimal integers representable in a uintmax_t, and the result must + * also be representable and non-negative. For divide and modulo, n2 must be non-zero. + * If any of these restrictions is violated, an error is triggered. + * + * Variables: + * (empty) - the result of the operation as a string representing a decimal number + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct boolean_instance { + NCDModuleInst *i; + int value; +}; + +typedef int (*boolean_compute_func) (uintmax_t n1, uintmax_t n2); + +struct number_instance { + NCDModuleInst *i; + uintmax_t value; +}; + +typedef int (*number_compute_func) (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out); + +static int compute_lesser (uintmax_t n1, uintmax_t n2) +{ + return n1 < n2; +} + +static int compute_greater (uintmax_t n1, uintmax_t n2) +{ + return n1 > n2; +} + +static int compute_lesser_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 <= n2; +} + +static int compute_greater_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 >= n2; +} + +static int compute_equal (uintmax_t n1, uintmax_t n2) +{ + return n1 == n2; +} + +static int compute_different (uintmax_t n1, uintmax_t n2) +{ + return n1 != n2; +} + +static int compute_add (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 > UINTMAX_MAX - n2) { + ModuleLog(i, BLOG_ERROR, "addition overflow"); + return 0; + } + *out = n1 + n2; + return 1; +} + +static int compute_subtract (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 < n2) { + ModuleLog(i, BLOG_ERROR, "subtraction underflow"); + return 0; + } + *out = n1 - n2; + return 1; +} + +static int compute_multiply (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n1 > UINTMAX_MAX / n2) { + ModuleLog(i, BLOG_ERROR, "multiplication overflow"); + return 0; + } + *out = n1 * n2; + return 1; +} + +static int compute_divide (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n2 == 0) { + ModuleLog(i, BLOG_ERROR, "division quotient is zero"); + return 0; + } + *out = n1 / n2; + return 1; +} + +static int compute_modulo (NCDModuleInst *i, uintmax_t n1, uintmax_t n2, uintmax_t *out) +{ + if (n2 == 0) { + ModuleLog(i, BLOG_ERROR, "modulo modulus is zero"); + return 0; + } + *out = n1 % n2; + return 1; +} + +static void new_boolean_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, boolean_compute_func cfunc) +{ + struct boolean_instance *o = vo; + o->i = i; + + NCDValRef n1_arg; + NCDValRef n2_arg; + if (!NCDVal_ListRead(params->args, 2, &n1_arg, &n2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(n1_arg) || !NCDVal_IsString(n2_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t n1; + if (!ncd_read_uintmax(n1_arg, &n1)) { + ModuleLog(o->i, BLOG_ERROR, "wrong first argument"); + goto fail0; + } + + uintmax_t n2; + if (!ncd_read_uintmax(n2_arg, &n2)) { + ModuleLog(o->i, BLOG_ERROR, "wrong second argument"); + goto fail0; + } + + o->value = cfunc(n1, n2); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int boolean_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct boolean_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void new_number_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, number_compute_func cfunc) +{ + struct number_instance *o = vo; + o->i = i; + + NCDValRef n1_arg; + NCDValRef n2_arg; + if (!NCDVal_ListRead(params->args, 2, &n1_arg, &n2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(n1_arg) || !NCDVal_IsString(n2_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t n1; + if (!ncd_read_uintmax(n1_arg, &n1)) { + ModuleLog(o->i, BLOG_ERROR, "wrong first argument"); + goto fail0; + } + + uintmax_t n2; + if (!ncd_read_uintmax(n2_arg, &n2)) { + ModuleLog(o->i, BLOG_ERROR, "wrong second argument"); + goto fail0; + } + + if (!cfunc(i, n1, n2, &o->value)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int number_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct number_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_uintmax(mem, o->value); + return 1; + } + + return 0; +} + +static void func_new_lesser (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_lesser); +} + +static void func_new_greater (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_greater); +} + +static void func_new_lesser_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_lesser_equal); +} + +static void func_new_greater_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_greater_equal); +} + +static void func_new_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_equal); +} + +static void func_new_different (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_boolean_templ(vo, i, params, compute_different); +} + +static void func_new_add (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_add); +} + +static void func_new_subtract (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_subtract); +} + +static void func_new_multiply (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_multiply); +} + +static void func_new_divide (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_divide); +} + +static void func_new_modulo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_number_templ(vo, i, params, compute_modulo); +} + +static struct NCDModule modules[] = { + { + .type = "num_lesser", + .func_new2 = func_new_lesser, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_greater", + .func_new2 = func_new_greater, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_lesser_equal", + .func_new2 = func_new_lesser_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_greater_equal", + .func_new2 = func_new_greater_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_equal", + .func_new2 = func_new_equal, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_different", + .func_new2 = func_new_different, + .func_getvar2 = boolean_func_getvar2, + .alloc_size = sizeof(struct boolean_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_add", + .func_new2 = func_new_add, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_subtract", + .func_new2 = func_new_subtract, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_multiply", + .func_new2 = func_new_multiply, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_divide", + .func_new2 = func_new_divide, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "num_modulo", + .func_new2 = func_new_modulo, + .func_getvar2 = number_func_getvar2, + .alloc_size = sizeof(struct number_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_arithmetic = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/assert.c b/external/badvpn_dns/ncd/modules/assert.c new file mode 100644 index 00000000..75205c5a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/assert.c @@ -0,0 +1,105 @@ +/** + * @file assert.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * assert(string cond) + * assert_false(string cond) + * + * Description: + * If 'cond' is equal to the string "true" (assert) or "false" (assert_false), + * does nothing. Otherwise, logs an error and initiates interpreter termination + * with exit code 1, i.e. it is equivalent to calling exit("1"). + * Note that "assert_false(cond);" is not completely equivalent to + * "not(cond) a; assert(a);", in case 'cond' is something other than "true" + * or "false". + */ + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new_common (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_false) +{ + // check arguments + NCDValRef cond_arg; + if (!NCDVal_ListRead(params->args, 1, &cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // if failed, initiate exit (before up!) + if ((!is_false && !NCDVal_StringEqualsId(cond_arg, NCD_STRING_TRUE, i->params->iparams->string_index)) || + (is_false && !NCDVal_StringEqualsId(cond_arg, NCD_STRING_FALSE, i->params->iparams->string_index)) + ) { + ModuleLog(i, BLOG_ERROR, "assertion failed"); + NCDModuleInst_Backend_InterpExit(i, 1); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(i, params, 0); +} + +static void func_new_false (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "assert", + .func_new2 = func_new + }, { + .type = "assert_false", + .func_new2 = func_new_false + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_assert = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/backtrack.c b/external/badvpn_dns/ncd/modules/backtrack.c new file mode 100644 index 00000000..ec9c5d7c --- /dev/null +++ b/external/badvpn_dns/ncd/modules/backtrack.c @@ -0,0 +1,103 @@ +/** + * @file backtrack.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * backtrack_point() + * backtrack_point::go() + * + * Description: + * The backtrack_point() statement creates a backtrack point, going up immedietely. + * The go() method triggers backtracking to the backtrack point, i.e. makes the + * backtrack_point() statement go down and back up at atomically. The go() method + * itself goes up immedietely, but side effects of triggering backtracking have + * priority. + */ + +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void go_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get backtrack point + NCDModuleInst *backtrack_point_inst = params->method_user; + + // go up (after toggling) + NCDModuleInst_Backend_Up(i); + + // toggle backtrack point + NCDModuleInst_Backend_DownUp(backtrack_point_inst); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "backtrack_point", + .func_new2 = func_new + }, { + .type = "backtrack_point::go", + .func_new2 = go_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_backtrack = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/blocker.c b/external/badvpn_dns/ncd/modules/blocker.c new file mode 100644 index 00000000..b742d725 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/blocker.c @@ -0,0 +1,353 @@ +/** + * @file blocker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Blocker module. Provides a statement that blocks when initialized, and which can be blocked + * and unblocked from outside. + * + * Synopsis: blocker() + * Description: provides blocking operations. Initially the blocking state is down (but this statement + * does not block). On deinitialization, waits for all corresponding use() statements + * to die before dying itself. + * + * Synopsis: blocker::up() + * Description: sets the blocking state to up. + * The immediate effects of corresponding use() statements going up are processed before + * this statement goes up; but this statement statement still goes up immediately, + * assuming the effects mentioned haven't resulted in the intepreter scheduling this + * very statement for destruction. + * + * Synopsis: blocker::down() + * Description: sets the blocking state to down. + * The immediate effects of corresponding use() statements going up are processed before + * this statement goes up; but this statement statement still goes up immediately, + * assuming the effects mentioned haven't resulted in the intepreter scheduling this + * very statement for destruction. + * + * Synopsis: blocker::downup() + * Description: atomically sets the blocker to down state (if it was up), then (back) to up state. + * Note that this is not equivalent to calling down() and immediately up(); in that case, + * the interpreter will first handle the immediate effects of any use() statements + * going down as a result of having called down() and will only later execute the up() + * statement. In fact, it is possible that the effects of down() will prevent up() from + * executing, which may leave the program in an undesirable state. + * + * Synopsis: blocker::rdownup() + * Description: on deinitialization, atomically sets the blocker to down state (if it was up), then + * (back) to up state. + * The immediate effects of corresponding use() statements changing state are processed + * *after* the immediate effects of this statement dying (in contrast to downup()). + * + * Synopsis: blocker::use() + * Description: blocks on the blocker. This module is in up state if and only if the blocking state of + * the blocker is up. Multiple use statements may be used with the same blocker. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + LinkedList1 users; + LinkedList0 rdownups_list; + int up; + int dying; +}; + +struct rdownup_instance { + NCDModuleInst *i; + struct instance *blocker; + LinkedList0Node rdownups_list_node; +}; + +struct use_instance { + NCDModuleInst *i; + struct instance *blocker; + LinkedList1Node blocker_node; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init users list + LinkedList1_Init(&o->users); + + // init rdownups list + LinkedList0_Init(&o->rdownups_list); + + // set not up + o->up = 0; + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->users)) + + // break any rdownups + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->rdownups_list)) { + struct rdownup_instance *rdu = UPPER_OBJECT(ln, struct rdownup_instance, rdownups_list_node); + ASSERT(rdu->blocker == o) + LinkedList0_Remove(&o->rdownups_list, &rdu->rdownups_list_node); + rdu->blocker = NULL; + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // if we have no users, die right away, else wait for users + if (LinkedList1_IsEmpty(&o->users)) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; +} + +static void updown_func_new_templ (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int up, int first_down) +{ + ASSERT(!first_down || up) + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (first_down || mo->up != up) { + // signal users + for (LinkedList1Node *node = LinkedList1_GetFirst(&mo->users); node; node = LinkedList1Node_Next(node)) { + struct use_instance *user = UPPER_OBJECT(node, struct use_instance, blocker_node); + ASSERT(user->blocker == mo) + if (first_down && mo->up) { + NCDModuleInst_Backend_Down(user->i); + } + if (up) { + NCDModuleInst_Backend_Up(user->i); + } else { + NCDModuleInst_Backend_Down(user->i); + } + } + + // change up state + mo->up = up; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void up_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 1, 0); +} + +static void down_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 0, 0); +} + +static void downup_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + updown_func_new_templ(i, params, 1, 1); +} + +static void rdownup_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct rdownup_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get blocker + struct instance *blk = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // set blocker + o->blocker = blk; + + // insert to rdownups list + LinkedList0_Prepend(&blk->rdownups_list, &o->rdownups_list_node); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rdownup_func_die (void *vo) +{ + struct rdownup_instance *o = vo; + + struct instance *blk = o->blocker; + + if (blk) { + // remove from rdownups list + LinkedList0_Remove(&blk->rdownups_list, &o->rdownups_list_node); + + // downup users + for (LinkedList1Node *ln = LinkedList1_GetFirst(&blk->users); ln; ln = LinkedList1Node_Next(ln)) { + struct use_instance *user = UPPER_OBJECT(ln, struct use_instance, blocker_node); + ASSERT(user->blocker == blk) + if (blk->up) { + NCDModuleInst_Backend_Down(user->i); + } + NCDModuleInst_Backend_Up(user->i); + } + + // set up + blk->up = 1; + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void use_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct use_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set blocker + o->blocker = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // add to blocker's list + LinkedList1_Append(&o->blocker->users, &o->blocker_node); + + // signal up if needed + if (o->blocker->up) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void use_func_die (void *vo) +{ + struct use_instance *o = vo; + + // remove from blocker's list + LinkedList1_Remove(&o->blocker->users, &o->blocker_node); + + // make the blocker die if needed + if (o->blocker->dying && LinkedList1_IsEmpty(&o->blocker->users)) { + instance_free(o->blocker); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "blocker", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "blocker::up", + .func_new2 = up_func_new + }, { + .type = "blocker::down", + .func_new2 = down_func_new + }, { + .type = "blocker::downup", + .func_new2 = downup_func_new + }, { + .type = "blocker::rdownup", + .func_new2 = rdownup_func_new, + .func_die = rdownup_func_die, + .alloc_size = sizeof(struct rdownup_instance) + }, { + .type = "blocker::use", + .func_new2 = use_func_new, + .func_die = use_func_die, + .alloc_size = sizeof(struct use_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_blocker = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/buffer.c b/external/badvpn_dns/ncd/modules/buffer.c new file mode 100644 index 00000000..eeb3715d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/buffer.c @@ -0,0 +1,619 @@ +/** + * @file buffer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * buffer([string data]) + * + * Variables: + * string (empty) - data in the buffer + * string length - number of bytes in the buffer + * + * Description: + * Implements an array of bytes which supports appending bytes and removing + * bytes from the beginning. The buffer is implemented using chunks; + * the time complexity of operations depends on the number of chunks affected, + * and not on the actual number of bytes. Each append operation produces a single + * chunk. In particular: + * + * Complexity of append and construction: + * log(total number of chunks) + (time for copying data). + * Complexity of consume: + * log(total number of chunks) * (1 + (number of chunks in consumed range)) + * Complexity of referencing and unreferencing a range: + * log(total number of chunks) * (1 + (number of chunks in referenced range)) + * + * Synopsis: + * buffer::append(string data) + * + * Description: + * Appends the given data to the end of the buffer. + * + * Synopsis: + * buffer::consume(string amount) + * + * Description: + * Removes the specified number of bytes from the beginning of the buffer. + * 'amount' must not be larger than the current length of the buffer. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct chunk; + +#include "buffer_chunks_tree.h" +#include + +struct buffer { + struct instance *inst; + ChunksTree chunks_tree; + int refcnt; +}; + +struct chunk { + struct buffer *buf; + size_t offset; + size_t length; + ChunksTreeNode chunks_tree_node; + int refcnt; + char data[]; +}; + +struct reference { + struct chunk *first_chunk; + size_t first_offset; + size_t length; + BRefTarget ref_target; +}; + +struct instance { + NCDModuleInst *i; + size_t offset; + size_t total_length; + struct buffer *buf; +}; + +#include "buffer_chunks_tree.h" +#include + +static void instance_assert (struct instance *inst); +static int instance_append (struct instance *inst, NCDValRef string); +static void instance_consume (struct instance *inst, size_t amount); +static struct buffer * buffer_init (struct instance *inst, NCDModuleInst *i); +static void buffer_free (struct buffer *buf); +static void buffer_detach (struct buffer *buf); +static struct chunk * buffer_get_existing_chunk (struct buffer *buf, size_t offset); +static struct chunk * chunk_init (struct instance *inst, size_t length); +static void chunk_unref (struct chunk *c); +static void chunk_assert (struct chunk *c); +static struct reference * reference_init (struct instance *inst, size_t offset, size_t length, NCDValComposedStringResource *out_resource); +static void reference_ref_target_func_release (BRefTarget *ref_target); +static void reference_assert (struct reference *ref); +static void reference_resource_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length); + +static void instance_assert (struct instance *inst) +{ + ASSERT(inst->buf->inst == inst) +} + +static int instance_append (struct instance *inst, NCDValRef string) +{ + instance_assert(inst); + ASSERT(NCDVal_IsString(string)) + + size_t length = NCDVal_StringLength(string); + + // if string is empty do nothing, we can't make an empty chunk + if (length == 0) { + return 1; + } + + // init chunk + struct chunk *c = chunk_init(inst, length); + if (!c) { + return 0; + } + + // copy data to chunk + NCDVal_StringCopyOut(string, 0, length, c->data); + + return 1; +} + +static void instance_consume (struct instance *inst, size_t amount) +{ + instance_assert(inst); + ASSERT(amount <= inst->total_length - inst->offset) + + // nothing do to if amount is zero + if (amount == 0) { + return; + } + + // find chunk where the byte in the buffer resides + struct chunk *c = buffer_get_existing_chunk(inst->buf, inst->offset); + + // increment buffer offset + inst->offset += amount; + + // unreference chunks which no longer contain buffer contents + while (c && c->offset + c->length <= inst->offset) { + struct chunk *next_c = ChunksTree_GetNext(&inst->buf->chunks_tree, 0, c); + chunk_unref(c); + c = next_c; + } +} + +static struct buffer * buffer_init (struct instance *inst, NCDModuleInst *i) +{ + ASSERT(inst) + + // allocate structure + struct buffer *buf = BAlloc(sizeof(*buf)); + if (!buf) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + return NULL; + } + + // set instance pointer + buf->inst = inst; + + // init chunks tree + ChunksTree_Init(&buf->chunks_tree); + + // set refcnt to 0 (number of reference objects) + buf->refcnt = 0; + + return buf; +} + +static void buffer_free (struct buffer *buf) +{ + ASSERT(!buf->inst) + ASSERT(ChunksTree_IsEmpty(&buf->chunks_tree)) + ASSERT(buf->refcnt == 0) + + // free structure + BFree(buf); +} + +static void buffer_detach (struct buffer *buf) +{ + ASSERT(buf->inst) + struct instance *inst = buf->inst; + + // consume entire buffer to free any chunks that aren't referenced + instance_consume(inst, inst->total_length - inst->offset); + + // clear instance pointer + buf->inst = NULL; + + // free buffer if there are no more chunks + if (ChunksTree_IsEmpty(&buf->chunks_tree)) { + buffer_free(buf); + } +} + +static struct chunk * buffer_get_existing_chunk (struct buffer *buf, size_t offset) +{ + struct chunk *c = ChunksTree_GetLastLesserEqual(&buf->chunks_tree, 0, offset); + + ASSERT(c) + chunk_assert(c); + ASSERT(offset >= c->offset) + ASSERT(offset < c->offset + c->length) + + return c; +} + +static struct chunk * chunk_init (struct instance *inst, size_t length) +{ + instance_assert(inst); + ASSERT(length > 0) + struct buffer *buf = inst->buf; + + // make sure length is not too large + if (length >= SIZE_MAX - inst->total_length) { + ModuleLog(inst->i, BLOG_ERROR, "length overflow"); + return NULL; + } + + // allocate structure + bsize_t size = bsize_add(bsize_fromsize(sizeof(struct chunk)), bsize_fromsize(length)); + struct chunk *c = BAllocSize(size); + if (!c) { + ModuleLog(inst->i, BLOG_ERROR, "BAllocSize failed"); + return NULL; + } + + // set some members + c->buf = buf; + c->offset = inst->total_length; + c->length = length; + + // insert into chunks tree + int res = ChunksTree_Insert(&buf->chunks_tree, 0, c, NULL); + B_ASSERT_USE(res) + + // set reference count to 1 (referenced by buffer contents) + c->refcnt = 1; + + // increment buffer length + inst->total_length += length; + + chunk_assert(c); + return c; +} + +static void chunk_unref (struct chunk *c) +{ + chunk_assert(c); + + // decrement reference count + c->refcnt--; + + // if reference count is not yet zero, do nothing else + if (c->refcnt > 0) { + return; + } + + // remove from chunks tree + ChunksTree_Remove(&c->buf->chunks_tree, 0, c); + + // free structure + BFree(c); +} + +static void chunk_assert (struct chunk *c) +{ + ASSERT(c->buf) + ASSERT(c->length > 0) + ASSERT(!c->buf->inst || c->offset <= c->buf->inst->total_length) + ASSERT(!c->buf->inst || c->length <= c->buf->inst->total_length - c->offset) + ASSERT(c->refcnt > 0) +} + +static struct reference * reference_init (struct instance *inst, size_t offset, size_t length, NCDValComposedStringResource *out_resource) +{ + instance_assert(inst); + struct buffer *buf = inst->buf; + ASSERT(offset >= inst->offset) + ASSERT(offset <= inst->total_length) + ASSERT(length <= inst->total_length - offset) + ASSERT(length > 0) + ASSERT(out_resource) + + // check buffer reference count. This ensures we can always increment the + // chunk reference counts, below. We use (INT_MAX - 1) here because the buffer + // itself can also own references to chunks. + if (buf->refcnt == INT_MAX - 1) { + ModuleLog(inst->i, BLOG_ERROR, "too many references"); + return NULL; + } + + // allocate structure + struct reference *ref = BAlloc(sizeof(*ref)); + if (!ref) { + ModuleLog(inst->i, BLOG_ERROR, "BAlloc failed"); + return NULL; + } + + // find chunk where the first byte of the interval resides + struct chunk *c = buffer_get_existing_chunk(buf, offset); + + // set some members + ref->first_chunk = c; + ref->first_offset = offset - c->offset; + ref->length = length; + + // increment buffer reference count + buf->refcnt++; + + // reference chunks + do { + struct chunk *next_c = ChunksTree_GetNext(&buf->chunks_tree, 0, c); + ASSERT(c->refcnt < INT_MAX) + c->refcnt++; + c = next_c; + } while (c && c->offset < offset + length); + + // init reference target + BRefTarget_Init(&ref->ref_target, reference_ref_target_func_release); + + // write resource + out_resource->func_getptr = reference_resource_func_getptr; + out_resource->user = ref; + out_resource->ref_target = &ref->ref_target; + + reference_assert(ref); + return ref; +} + +static void reference_ref_target_func_release (BRefTarget *ref_target) +{ + struct reference *ref = UPPER_OBJECT(ref_target, struct reference, ref_target); + reference_assert(ref); + struct buffer *buf = ref->first_chunk->buf; + + // compute offset + size_t offset = ref->first_chunk->offset + ref->first_offset; + + // unreference chunks + struct chunk *c = ref->first_chunk; + do { + struct chunk *next_c = ChunksTree_GetNext(&buf->chunks_tree, 0, c); + chunk_unref(c); + c = next_c; + } while (c && c->offset < offset + ref->length); + + // decrement buffer reference count + ASSERT(buf->refcnt > 0) + buf->refcnt--; + + // free structure + BFree(ref); + + // if the instance has died and there are no more chunks, free buffer + if (!buf->inst && ChunksTree_IsEmpty(&buf->chunks_tree)) { + buffer_free(buf); + } +} + +static void reference_assert (struct reference *ref) +{ + ASSERT(ref->first_chunk) + ASSERT(ref->first_offset < ref->first_chunk->length) + ASSERT(ref->length > 0) + chunk_assert(ref->first_chunk); +} + +static void reference_resource_func_getptr (void *user, size_t offset, const char **out_data, size_t *out_length) +{ + struct reference *ref = user; + reference_assert(ref); + ASSERT(offset < ref->length) + ASSERT(out_data) + ASSERT(out_length) + + // compute absolute offset of request + size_t abs_offset = ref->first_chunk->offset + ref->first_offset + offset; + + // find chunk where the byte at the requested offset resides + struct chunk *c = buffer_get_existing_chunk(ref->first_chunk->buf, abs_offset); + + // compute offset of this byte within the chunk + size_t chunk_offset = abs_offset - c->offset; + + // return the data from this byte to the end of the chunk + *out_data = c->data + chunk_offset; + *out_length = c->length - chunk_offset; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // pass instance pointer to methods + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + NCDValRef data_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 0) && + !NCDVal_ListRead(params->args, 1, &data_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsInvalid(data_arg) && !NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // set offset and total length + o->offset = 0; + o->total_length = 0; + + // allocate buffer + o->buf = buffer_init(o, i); + if (!o->buf) { + goto fail0; + } + + // append initial data + if (!NCDVal_IsInvalid(data_arg)) { + if (!instance_append(o, data_arg)) { + goto fail1; + } + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + o->buf->inst = NULL; + buffer_free(o->buf); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_assert(o); + + // detach buffer from instance + buffer_detach(o->buf); + + // die + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + instance_assert(o); + + if (name == NCD_STRING_EMPTY) { + if (o->total_length - o->offset == 0) { + *out = NCDVal_NewStringUninitialized(mem, 0); + } else { + NCDValComposedStringResource resource; + struct reference *ref = reference_init(o, o->offset, o->total_length - o->offset, &resource); + if (!ref) { + goto fail; + } + *out = NCDVal_NewComposedString(mem, resource, 0, ref->length); + BRefTarget_Deref(resource.ref_target); + } + return 1; + } + + if (name == NCD_STRING_LENGTH) { + *out = ncd_make_uintmax(mem, o->total_length - o->offset); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void append_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get instance + struct instance *inst = params->method_user; + + // append + if (!instance_append(inst, data_arg)) { + ModuleLog(i, BLOG_ERROR, "instance_append failed"); + goto fail0; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void consume_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef amount_arg; + if (!NCDVal_ListRead(params->args, 1, &amount_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(amount_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse amount + uintmax_t amount; + if (!ncd_read_uintmax(amount_arg, &amount)) { + ModuleLog(i, BLOG_ERROR, "wrong amount"); + goto fail0; + } + + // get instance + struct instance *inst = params->method_user; + + // check amount + if (amount > inst->total_length - inst->offset) { + ModuleLog(i, BLOG_ERROR, "amount is more than buffer length"); + goto fail0; + } + + // consume + instance_consume(inst, amount); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "buffer", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "buffer::append", + .func_new2 = append_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "buffer::consume", + .func_new2 = consume_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_buffer = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/buffer_chunks_tree.h b/external/badvpn_dns/ncd/modules/buffer_chunks_tree.h new file mode 100644 index 00000000..c299d7c1 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/buffer_chunks_tree.h @@ -0,0 +1,9 @@ +#define SAVL_PARAM_NAME ChunksTree +#define SAVL_PARAM_FEATURE_COUNTS 0 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct chunk +#define SAVL_PARAM_TYPE_KEY size_t +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) B_COMPARE((entry1)->offset, (entry2)->offset) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) B_COMPARE((key1), (entry2)->offset) +#define SAVL_PARAM_MEMBER_NODE chunks_tree_node diff --git a/external/badvpn_dns/ncd/modules/call2.c b/external/badvpn_dns/ncd/modules/call2.c new file mode 100644 index 00000000..f3e65bac --- /dev/null +++ b/external/badvpn_dns/ncd/modules/call2.c @@ -0,0 +1,498 @@ +/** + * @file call2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * call(string template, list args) + * + * Description: + * Calls a process template. The 'template' argument is the name of the process + * template to call, and the 'list' argument is a list of arguments for the + * process template. Calling a process template is roughly equivalent to placing + * the statements within that template into the place of call(), except for the + * points presented next. The 'template' argument can be a special value "", + * which makes call() a no-op. + * + * The process created from the called template will be able to access the arguments + * that were given in the 'args' argument to call() via the '_argN' predefined\ + * objects (e.g. _arg0 for the first argumens), and also via '_args' for the entire + * argument list. + * + * The called process also will be able to access objects within the calling + * process as seen by the call() statement. However such any access needs to happen + * via a special '_caller' predefined object. For example, if there is a statement + * 'var("a") x;' somewhere above the call() statement, the called process can access + * it as '_caller.x'. + * + * Note that call() preserves backtracking semantics, i.e. when a statement within + * the called process goes down after having gone up, the behaviour really is as + * if the call() statement was replaced with the statements in the called template, + * (disregarding variable resolution). + * + * Because the template name is an argument, call() can be used for branching. + * For example, if we have an object 'x' with the value "true" or "false", a + * branch can be performed by defining two process templates, 'branch_true' + * and 'branch_false', and branching with the following code: + * + * concat("branch_", x) name; + * call(name, {}); + * + * Synopsis: + * call_with_caller_target(string template, list args, string caller_target) + * + * Description: + * Like call(), except that the target of the '_caller' predefined object is + * specified by the 'caller_target' argument. This is indented to be used from + * generic code for user-specified callbacks, allowing the user to easily refer to + * his own objects from inside the callback. + * + * The 'caller_target' must be a non-empty string referring to an actual object; + * there is no choice of 'caller_target' that would make call_with_caller_target() + * equivalent to call(). + * + * Synopsis: + * embcall2_multif(string cond1, string template1, ..., [string else_template]) + * + * Description: + * This is an internal command used to implement the 'If' clause. The arguments + * are pairs of (cond, template), where 'cond' is a condition in form of a string, + * and 'template' is the name of the process template for this condition. The + * template corresponding to the first condition equal to "true" is called; if + * there is no true condition, either the template 'else_template' is called, + * if it is provided, or nothing is performed, if 'else_template' is not provided. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_WORKING 1 +#define STATE_UP 2 +#define STATE_WAITING 3 +#define STATE_TERMINATING 4 +#define STATE_NONE 5 + +#define NUM_STATIC_NAMES 4 + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + int state; +}; + +struct instance_with_caller_target { + struct instance base; + NCD_string_id_t *dynamic_names; + size_t num_names; + NCD_string_id_t static_names[NUM_STATIC_NAMES]; +}; + +#define NAMES_PARAM_NAME CallNames +#define NAMES_PARAM_TYPE struct instance_with_caller_target +#define NAMES_PARAM_MEMBER_DYNAMIC_NAMES dynamic_names +#define NAMES_PARAM_MEMBER_STATIC_NAMES static_names +#define NAMES_PARAM_MEMBER_NUM_NAMES num_names +#define NAMES_PARAM_NUM_STATIC_NAMES NUM_STATIC_NAMES +#include + +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, int embed); +static void instance_free (struct instance *o); + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_WORKING) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = STATE_UP; + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_UP) + + // signal down + NCDModuleInst_Backend_Down(o->i); + + // set state waiting + o->state = STATE_WAITING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_TERMINATING) + + // die finally + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj_embed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int process_func_getspecialobj_noembed (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int process_func_getspecialobj_with_caller_target (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj_with_caller_target); + return 1; + } + + return 0; +} + +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int caller_obj_func_getobj_with_caller_target (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance_with_caller_target *o_ch = NCDObject_DataPtr(obj); + ASSERT(o_ch->num_names > 0) + + NCD_string_id_t *names = CallNames_GetNames(o_ch); + + NCDObject object; + if (!NCDModuleInst_Backend_GetObj(o_ch->base.i, names[0], &object)) { + return 0; + } + + NCDObject obj2; + if (!NCDObject_ResolveObjExprCompact(&object, names + 1, o_ch->num_names - 1, &obj2)) { + return 0; + } + + if (name == NCD_STRING_EMPTY) { + *out_object = obj2; + return 1; + } + + return NCDObject_GetObj(&obj2, name, out_object); +} + +static void func_new_templ (void *vo, NCDModuleInst *i, NCDValRef template_name, NCDValRef args, int embed) +{ + ASSERT(NCDVal_IsInvalid(template_name) || NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(embed == !!embed) + + struct instance *o = vo; + o->i = i; + + if (NCDVal_IsInvalid(template_name) || ncd_is_none(template_name)) { + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state none + o->state = STATE_NONE; + } else { + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special functions + if (embed) { + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_embed); + } else { + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_noembed); + } + + // set state working + o->state = STATE_WORKING; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + // free process + if (o->state != STATE_NONE) { + NCDModuleProcess_Free(&o->process); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_new_call (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef template_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_arg, &args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + func_new_templ(vo, i, template_arg, args_arg, 0); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_call_with_caller_target (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + struct instance_with_caller_target *o_ct = vo; + o->i = i; + + NCDValRef template_arg; + NCDValRef args_arg; + NCDValRef caller_target_arg; + if (!NCDVal_ListRead(params->args, 3, &template_arg, &args_arg, &caller_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_arg) || !NCDVal_IsList(args_arg) || !NCDVal_IsString(caller_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValContString cts; + if (!NCDVal_StringContinuize(caller_target_arg, &cts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringContinuize failed"); + goto fail0; + } + + int res = CallNames_InitNames(o_ct, i->params->iparams->string_index, cts.data, NCDVal_StringLength(caller_target_arg)); + NCDValContString_Free(&cts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "CallerNames_InitNames failed"); + goto fail0; + } + + if (ncd_is_none(template_arg)) { + // signal up + NCDModuleInst_Backend_Up(i); + + // set state none + o->state = STATE_NONE; + } else { + // create process + if (!NCDModuleProcess_InitValue(&o->process, i, template_arg, args_arg, process_handler_event)) { + ModuleLog(i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail1; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj_with_caller_target); + + // set state working + o->state = STATE_WORKING; + } + + return; + +fail1: + CallNames_FreeNames(o_ct); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_embcall_multif (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef args = params->args; + + NCDValRef template_value = NCDVal_NewInvalid(); + + size_t count = NCDVal_ListCount(args); + size_t j = 0; + + while (j < count) { + NCDValRef arg = NCDVal_ListGet(args, j); + + if (j == count - 1) { + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "bad arguments"); + goto fail0; + } + + template_value = arg; + break; + } + + NCDValRef arg2 = NCDVal_ListGet(args, j + 1); + + if (!NCDVal_IsString(arg) || !NCDVal_IsString(arg2)) { + ModuleLog(i, BLOG_ERROR, "bad arguments"); + goto fail0; + } + + if (ncd_read_boolean(arg)) { + template_value = arg2; + break; + } + + j += 2; + } + + func_new_templ(vo, i, template_value, NCDVal_NewInvalid(), 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_TERMINATING) + + // if none, die now + if (o->state == STATE_NONE) { + instance_free(o); + return; + } + + // request process to terminate + NCDModuleProcess_Terminate(&o->process); + + // set state terminating + o->state = STATE_TERMINATING; +} + +static void func_die_with_caller_target (void *vo) +{ + struct instance_with_caller_target *o_ct = vo; + + CallNames_FreeNames(o_ct); + + func_die(vo); +} + +static void func_clean (void *vo) +{ + struct instance *o = vo; + if (o->state != STATE_WAITING) { + return; + } + + // allow process to continue + NCDModuleProcess_Continue(&o->process); + + // set state working + o->state = STATE_WORKING; +} + +static int func_getobj (void *vo, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = vo; + + if (o->state == STATE_NONE) { + return 0; + } + + return NCDModuleProcess_GetObj(&o->process, name, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "call", + .func_new2 = func_new_call, + .func_die = func_die, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance) + }, { + .type = "call_with_caller_target", + .func_new2 = func_new_call_with_caller_target, + .func_die = func_die_with_caller_target, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance_with_caller_target) + }, { + .type = "embcall2_multif", + .func_new2 = func_new_embcall_multif, + .func_die = func_die, + .func_clean = func_clean, + .func_getobj = func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN|NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_call2 = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/choose.c b/external/badvpn_dns/ncd/modules/choose.c new file mode 100644 index 00000000..23f8bbeb --- /dev/null +++ b/external/badvpn_dns/ncd/modules/choose.c @@ -0,0 +1,145 @@ +/** + * @file choose.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Multiple value selection based on boolean conditions. + * + * Synopsis: + * choose({{string cond1, result1}, ..., {string condN, resultN}}, default_result) + * + * Variables: + * (empty) - If cond1="true" then result1, + * else if cond2="true" then result2, + * ..., + * else default_result. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValRef result; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_choices; + NCDValRef arg_default_result; + if (!NCDVal_ListRead(params->args, 2, &arg_choices, &arg_default_result)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(arg_choices)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // iterate choices + int have_result = 0; + size_t count = NCDVal_ListCount(arg_choices); + for (size_t j = 0; j < count; j++) { + NCDValRef c = NCDVal_ListGet(arg_choices, j); + + // check choice type + if (!NCDVal_IsList(c)) { + ModuleLog(i, BLOG_ERROR, "wrong choice type"); + goto fail0; + } + + // read choice + NCDValRef c_cond; + NCDValRef c_result; + if (!NCDVal_ListRead(c, 2, &c_cond, &c_result)) { + ModuleLog(i, BLOG_ERROR, "wrong choice contents arity"); + goto fail0; + } + if (!NCDVal_IsString(c_cond)) { + ModuleLog(i, BLOG_ERROR, "wrong choice condition type"); + goto fail0; + } + + // update result + if (!have_result && ncd_read_boolean(c_cond)) { + o->result = c_result; + have_result = 1; + } + } + + // default? + if (!have_result) { + o->result = arg_default_result; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->result); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "choose", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_choose = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/command_template.c b/external/badvpn_dns/ncd/modules/command_template.c new file mode 100644 index 00000000..6eb92f6f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/command_template.c @@ -0,0 +1,218 @@ +/** + * @file command_template.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#define STATE_ADDING_LOCK 1 +#define STATE_ADDING 2 +#define STATE_ADDING_NEED_DELETE 3 +#define STATE_DONE 4 +#define STATE_DELETING_LOCK 5 +#define STATE_DELETING 6 + +static void lock_handler (command_template_instance *o); +static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status); +static void free_template (command_template_instance *o, int is_error); + +static void lock_handler (command_template_instance *o) +{ + ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_DELETING_LOCK) + ASSERT(!(o->state == STATE_ADDING_LOCK) || o->do_exec) + ASSERT(!(o->state == STATE_DELETING_LOCK) || o->undo_exec) + + if (o->state == STATE_ADDING_LOCK) { + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->do_exec, CmdLine_Get(&o->do_cmdline), NULL)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); + free_template(o, 1); + return; + } + + // set state + o->state = STATE_ADDING; + } else { + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, o->undo_exec, CmdLine_Get(&o->undo_cmdline), NULL)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "BProcess_Init failed"); + free_template(o, 1); + return; + } + + // set state + o->state = STATE_DELETING; + } +} + +static void process_handler (command_template_instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_ADDING || o->state == STATE_ADDING_NEED_DELETE || o->state == STATE_DELETING) + + // release lock + BEventLockJob_Release(&o->elock_job); + + // free process + BProcess_Free(&o->process); + + if (!normally || normally_exit_status != 0) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "command failed"); + + free_template(o, 1); + return; + } + + switch (o->state) { + case STATE_ADDING: { + // set state + o->state = STATE_DONE; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } break; + + case STATE_ADDING_NEED_DELETE: { + if (o->undo_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_DELETING_LOCK; + } else { + free_template(o, 0); + return; + } + } break; + + case STATE_DELETING: { + // finish + free_template(o, 0); + return; + } break; + } +} + +void command_template_new (command_template_instance *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock) +{ + // init arguments + o->i = i; + o->free_func = free_func; + o->user = user; + o->blog_channel = blog_channel; + + // build do command + if (!build_cmdline(o->i, params->args, 0, &o->do_exec, &o->do_cmdline)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline do callback failed"); + goto fail0; + } + + // build undo command + if (!build_cmdline(o->i, params->args, 1, &o->undo_exec, &o->undo_cmdline)) { + NCDModuleInst_Backend_Log(o->i, o->blog_channel, BLOG_ERROR, "build_cmdline undo callback failed"); + goto fail1; + } + + // init lock job + BEventLockJob_Init(&o->elock_job, elock, (BEventLock_handler)lock_handler, o); + + if (o->do_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_ADDING_LOCK; + } else { + // set state + o->state = STATE_DONE; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail1: + if (o->do_exec) { + free(o->do_exec); + CmdLine_Free(&o->do_cmdline); + } +fail0: + o->free_func(o->user, 1); +} + +static void free_template (command_template_instance *o, int is_error) +{ + // free lock job + BEventLockJob_Free(&o->elock_job); + + // free undo command + if (o->undo_exec) { + free(o->undo_exec); + CmdLine_Free(&o->undo_cmdline); + } + + // free do command + if (o->do_exec) { + free(o->do_exec); + CmdLine_Free(&o->do_cmdline); + } + + // call free function + o->free_func(o->user, is_error); +} + +void command_template_die (command_template_instance *o) +{ + ASSERT(o->state == STATE_ADDING_LOCK || o->state == STATE_ADDING || o->state == STATE_DONE) + + switch (o->state) { + case STATE_ADDING_LOCK: { + free_template(o, 0); + return; + } break; + + case STATE_ADDING: { + // set state + o->state = STATE_ADDING_NEED_DELETE; + } break; + + case STATE_DONE: { + if (o->undo_exec) { + // wait for lock + BEventLockJob_Wait(&o->elock_job); + + // set state + o->state = STATE_DELETING_LOCK; + } else { + free_template(o, 0); + return; + } + } break; + } +} diff --git a/external/badvpn_dns/ncd/modules/command_template.h b/external/badvpn_dns/ncd/modules/command_template.h new file mode 100644 index 00000000..e1228ae8 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/command_template.h @@ -0,0 +1,62 @@ +/** + * @file command_template.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Template for a module which executes a command to start and stop. + * The command is executed asynchronously. + */ + +#ifndef BADVPN_NCD_MODULES_COMMAND_TEMPLATE_H +#define BADVPN_NCD_MODULES_COMMAND_TEMPLATE_H + +#include +#include +#include + +typedef int (*command_template_build_cmdline) (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl); +typedef void (*command_template_free_func) (void *user, int is_error); + +typedef struct { + NCDModuleInst *i; + command_template_free_func free_func; + void *user; + int blog_channel; + char *do_exec; + CmdLine do_cmdline; + char *undo_exec; + CmdLine undo_cmdline; + BEventLockJob elock_job; + int state; + BProcess process; +} command_template_instance; + +void command_template_new (command_template_instance *o, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline, command_template_free_func free_func, void *user, int blog_channel, BEventLock *elock); +void command_template_die (command_template_instance *o); + +#endif diff --git a/external/badvpn_dns/ncd/modules/concat.c b/external/badvpn_dns/ncd/modules/concat.c new file mode 100644 index 00000000..e71e69e8 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/concat.c @@ -0,0 +1,189 @@ +/** + * @file concat.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * concat([string elem ...]) + * concatv(list strings) + * + * Description: + * Concatenates zero or more strings. The result is available as the empty + * string variable. For concatv(), the strings are provided as a single + * list argument. For concat(), the strings are provided as arguments + * themselves. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct result { + BRefTarget ref_target; + size_t length; + char data[]; +}; + +struct instance { + NCDModuleInst *i; + struct result *result; +}; + +static void result_ref_target_func_release (BRefTarget *ref_target) +{ + struct result *result = UPPER_OBJECT(ref_target, struct result, ref_target); + + BFree(result); +} + +static void new_concat_common (void *vo, NCDModuleInst *i, NCDValRef list) +{ + ASSERT(NCDVal_IsList(list)) + struct instance *o = vo; + o->i = i; + + size_t count = NCDVal_ListCount(list); + bsize_t result_size = bsize_fromsize(sizeof(struct result)); + + // check arguments and compute result size + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + result_size = bsize_add(result_size, bsize_fromsize(NCDVal_StringLength(arg))); + } + + // allocate result + o->result = BAllocSize(result_size); + if (!o->result) { + ModuleLog(i, BLOG_ERROR, "BAllocSize failed"); + goto fail0; + } + + // init ref target + BRefTarget_Init(&o->result->ref_target, result_ref_target_func_release); + + // copy data to result + o->result->length = 0; + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + b_cstring cstr = NCDVal_StringCstring(arg); + b_cstring_copy_to_buf(cstr, 0, cstr.length, o->result->data + o->result->length); + o->result->length += cstr.length; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_concat (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_concat_common(vo, i, params->args); +} + +static void func_new_concatv (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef list_arg; + if (!NCDVal_ListRead(params->args, 1, &list_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(list_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + new_concat_common(vo, i, list_arg); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // release result reference + BRefTarget_Deref(&o->result->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, o->result->data, o->result->length, &o->result->ref_target); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "concat", + .func_new2 = func_new_concat, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "concatv", + .func_new2 = func_new_concatv, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_concat = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/daemon.c b/external/badvpn_dns/ncd/modules/daemon.c new file mode 100644 index 00000000..3dac4c5e --- /dev/null +++ b/external/badvpn_dns/ncd/modules/daemon.c @@ -0,0 +1,296 @@ +/** + * @file daemon.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Runs a program in the background, restarting it if it crashes. + * On deinitialization, sends SIGTERM to the daemon and waits for it to terminate + * (unless it's crashed at the time). + * + * Synopsis: + * daemon(list(string) cmd [, map options]) + * + * Arguments: + * cmd - Command for the daemon. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). + * options - Map of options: + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define RETRY_TIME 10000 + +#define STATE_RETRYING 1 +#define STATE_RUNNING 2 +#define STATE_RUNNING_DIE 3 + +struct instance { + NCDModuleInst *i; + NCDValRef cmd_arg; + NCDBProcessOpts opts; + BTimer timer; + BProcess process; + int state; +}; + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl); +static void start_process (struct instance *o); +static void timer_handler (struct instance *o); +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status); +static void instance_free (struct instance *o); + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + + if (!NCDVal_IsList(cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + ModuleLog(i, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, cstr, 0, cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void start_process (struct instance *o) +{ + // build cmdline + char *exec; + CmdLine cl; + if (!build_cmdline(o->i, o->cmd_arg, &exec, &cl)) { + goto fail; + } + + // start process + struct BProcess_params p_params = NCDBProcessOpts_GetParams(&o->opts); + int res = BProcess_Init2(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, exec, CmdLine_Get(&cl), p_params); + CmdLine_Free(&cl); + free(exec); + + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BProcess_Init2 failed"); + goto fail; + } + + // set state running + o->state = STATE_RUNNING; + return; + +fail: + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); + + // set state retrying + o->state = STATE_RETRYING; +} + +static void timer_handler (struct instance *o) +{ + ASSERT(o->state == STATE_RETRYING) + + ModuleLog(o->i, BLOG_INFO, "restarting after crash"); + + start_process(o); +} + +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_RUNNING || o->state == STATE_RUNNING_DIE) + + // free process + BProcess_Free(&o->process); + + // if we were requested to die, die now + if (o->state == STATE_RUNNING_DIE) { + instance_free(o); + return; + } + + BLog(BLOG_ERROR, "daemon crashed"); + + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); + + // set state retrying + o->state = STATE_RETRYING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &o->cmd_arg) && + !NCDVal_ListRead(params->args, 2, &o->cmd_arg, &opts_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init options + if (!NCDBProcessOpts_Init(&o->opts, opts_arg, NULL, NULL, i, BLOG_CURRENT_CHANNEL)) { + goto fail0; + } + + // init timer + BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o); + + // signal up + NCDModuleInst_Backend_Up(i); + + // try starting process + start_process(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + // free options + NCDBProcessOpts_Free(&o->opts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_RUNNING_DIE) + + // if not running, die immediately + if (o->state == STATE_RETRYING) { + instance_free(o); + return; + } + + // request termination + BProcess_Terminate(&o->process); + + // set state running die + o->state = STATE_RUNNING_DIE; +} + +static struct NCDModule modules[] = { + { + .type = "daemon", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_daemon = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/depend.c b/external/badvpn_dns/ncd/modules/depend.c new file mode 100644 index 00000000..5019cf4f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/depend.c @@ -0,0 +1,452 @@ +/** + * @file depend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dependencies module. + * + * Synopsis: provide(string name) + * Description: Provides a resource. On initialization, transitions any depend()-s + * waiting for this resource to UP state. On deinitialization, transitions + * depend()-s using this resource to DOWN state, and waits for all of them to + * receive the clean signal (i.e. wait for all of the statements following them in + * their processes to terminate). Initialization fails if a provide() already + * exists for this resource (including if it is being deinitialized). + * + * Synopsis: provide_event(string name) + * Description: Like provide(), but if another provide() already exists for this + * resource, initialization does not fail, and the request is queued to the active + * provide() for this resource. When an active provide() disappears that has + * queued provide()-s, one of them is promoted to be the active provide() for this + * resource, and the remaining queue is transferred to it. + * (mentions of provide() in this text also apply to provide_event()) + * + * Synopsis: depend(string name) + * Description: Depends on a resource. Is in UP state when a provide() + * for this resource is available, and in DOWN state when it is not (either + * it does not exist or is being terminated). + * Variables: Provides variables available from the corresponding provide, + * ("modname.varname" or "modname"). + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide { + NCDModuleInst *i; + const char *name; + size_t name_len; + int is_queued; + union { + struct { + LinkedList3Node queued_node; // node in list which begins with provide.queued_provides_firstnode + }; + struct { + LinkedList1Node provides_node; // node in provides + LinkedList1 depends; + LinkedList3Node queued_provides_firstnode; + int dying; + }; + }; +}; + +struct depend { + NCDModuleInst *i; + const char *name; + size_t name_len; + struct provide *p; + LinkedList1Node node; +}; + +struct global { + LinkedList1 provides; + LinkedList1 free_depends; +}; + +static struct provide * find_provide (struct global *g, const char *name, size_t name_len) +{ + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->provides); n; n = LinkedList1Node_Next(n)) { + struct provide *p = UPPER_OBJECT(n, struct provide, provides_node); + ASSERT(!p->is_queued) + + if (p->name_len == name_len && !memcmp(p->name, name, name_len)) { + return p; + } + } + + return NULL; +} + +static void provide_promote (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(!find_provide(g, o->name, o->name_len)) + + // set not queued + o->is_queued = 0; + + // insert to provides list + LinkedList1_Append(&g->provides, &o->provides_node); + + // init depends list + LinkedList1_Init(&o->depends); + + // set not dying + o->dying = 0; + + // attach free depends with this name + LinkedList1Node *n = LinkedList1_GetFirst(&g->free_depends); + while (n) { + LinkedList1Node *next = LinkedList1Node_Next(n); + struct depend *d = UPPER_OBJECT(n, struct depend, node); + ASSERT(!d->p) + + if (d->name_len != o->name_len || memcmp(d->name, o->name, d->name_len)) { + n = next; + continue; + } + + // remove from free depends list + LinkedList1_Remove(&g->free_depends, &d->node); + + // insert to provide's list + LinkedList1_Append(&o->depends, &d->node); + + // set provide + d->p = o; + + // signal up + NCDModuleInst_Backend_Up(d->i); + + n = next; + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init provides list + LinkedList1_Init(&g->provides); + + // init free depends list + LinkedList1_Init(&g->free_depends); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->free_depends)) + ASSERT(LinkedList1_IsEmpty(&g->provides)) + + // free global state structure + BFree(g); +} + +static void provide_func_new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int event) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->name = NCDVal_StringData(name_arg); + o->name_len = NCDVal_StringLength(name_arg); + + // signal up. + // This comes above provide_promote(), so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // check for existing provide with this name + struct provide *ep = find_provide(g, o->name, o->name_len); + if (ep) { + ASSERT(!ep->is_queued) + + if (!event) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // set queued + o->is_queued = 1; + + // insert to existing provide's queued provides list + LinkedList3Node_InitAfter(&o->queued_node, &ep->queued_provides_firstnode); + } else { + // init first node for queued provides list + LinkedList3Node_InitLonely(&o->queued_provides_firstnode); + + // promote provide + provide_promote(o); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + provide_func_new_templ(vo, i, params, 0); +} + +static void provide_event_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + provide_func_new_templ(vo, i, params, 1); +} + +static void provide_free (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(o->is_queued || LinkedList1_IsEmpty(&o->depends)) + + if (o->is_queued) { + // remove from existing provide's queued provides list + LinkedList3Node_Free(&o->queued_node); + } else { + // remove from provides list + LinkedList1_Remove(&g->provides, &o->provides_node); + + // if we have provides queued, promote the first one + if (LinkedList3Node_Next(&o->queued_provides_firstnode)) { + // get first queued provide + struct provide *qp = UPPER_OBJECT(LinkedList3Node_Next(&o->queued_provides_firstnode), struct provide, queued_node); + ASSERT(qp->is_queued) + + // make it the head of the queued provides list + LinkedList3Node_Free(&qp->queued_node); + LinkedList3Node_InitAfter(&qp->queued_provides_firstnode, &o->queued_provides_firstnode); + LinkedList3Node_Free(&o->queued_provides_firstnode); + + // promote provide + provide_promote(qp); + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(o->is_queued || !o->dying) + + // if we are queued or have no depends, die immediately + if (o->is_queued || LinkedList1_IsEmpty(&o->depends)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // signal our depends down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->depends); n; n = LinkedList1Node_Next(n)) { + struct depend *d = UPPER_OBJECT(n, struct depend, node); + ASSERT(d->p == o) + + // signal down + NCDModuleInst_Backend_Down(d->i); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->name = NCDVal_StringData(name_arg); + o->name_len = NCDVal_StringLength(name_arg); + + // find a provide with our name + struct provide *p = find_provide(g, o->name, o->name_len); + ASSERT(!p || !p->is_queued) + + if (p && !p->dying) { + // insert to provide's list + LinkedList1_Append(&p->depends, &o->node); + + // set provide + o->p = p; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } else { + // insert to free depends list + LinkedList1_Append(&g->free_depends, &o->node); + + // set no provide + o->p = NULL; + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_free (struct depend *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(!o->p || !o->p->is_queued) + + if (o->p) { + // remove from provide's list + LinkedList1_Remove(&o->p->depends, &o->node); + + // if provide is dying and is empty, let it die + if (o->p->dying && LinkedList1_IsEmpty(&o->p->depends)) { + provide_free(o->p); + } + } else { + // remove free depends list + LinkedList1_Remove(&g->free_depends, &o->node); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + + depend_free(o); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + struct global *g = ModuleGlobal(o->i); + ASSERT(!o->p || !o->p->is_queued) + + if (!(o->p && o->p->dying)) { + return; + } + + struct provide *p = o->p; + + // remove from provide's list + LinkedList1_Remove(&p->depends, &o->node); + + // insert to free depends list + LinkedList1_Append(&g->free_depends, &o->node); + + // set no provide + o->p = NULL; + + // if provide is empty, let it die + if (LinkedList1_IsEmpty(&p->depends)) { + provide_free(p); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + ASSERT(!o->p || !o->p->is_queued) + + if (!o->p) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->p->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "provide_event", + .func_new2 = provide_event_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_depend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/depend_scope.c b/external/badvpn_dns/ncd/modules/depend_scope.c new file mode 100644 index 00000000..1a814a0d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/depend_scope.c @@ -0,0 +1,466 @@ +/** + * @file depend_scope.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Multiple-option dependencies module. + * + * Synopsis: + * depend_scope() + * + * Description: + * A scope for dependency names. Any dependency names used in provide() and depend() + * methods on this object are local to this object. Contrast to the multidepend module, + * which provides the same functionality as this module, but with a single global + * dependency name scope. + * + * Synopsis: + * depend_scope::provide(name) + * + * Arguments: + * name - provider identifier + * + * Description: + * Satisfies a dependency. + * If any depend()'s get immediately bound to this provide(), + * the side effects of those first happen, and only then can the process of this + * provide() continue. + * When provide() is requested to deinitialize, if there are any depend()'s bound, + * provide() will not finish deinitializing until all the processes containing the + * bound depend()'s have backtracked to the point of the corresponding depend(). + * More specifically, when backtracking has finished for the last bound depend(), + * first the immediate effects of the provide() finshing deinitialization will happen, + * and only then will the depend() attempt to rebind. (If the converse was true, the + * depend() could rebind, but when deinitialization of the provide()'s process + * continues, lose this binding. See ncd/tests/depend_scope.ncd .) + * + * Synopsis: + * depend_scope::depend(list names) + * + * Arguments: + * names - list of provider identifiers. Names more to the beginning are considered + * more desirable. + * + * Description: + * Binds to the provide() providing one of the specified dependency names which is most + * desirable. If there is no provide() providing any of the given dependency names, + * waits and binds when one becomes available. + * If the depend() is bound to a provide(), and the bound provide() is requested to + * deinitize, or a more desirable provide() becomes available, the depend() statement + * will go down (triggering backtracking), wait for backtracking to finish, and then + * try to bind to a provide() again, as if it was just initialized. + * When depend() is requested to deinitialize, it deinitializes immediately. + * + * Attributes: + * Exposes objects as seen from the corresponding provide. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct scope { + BRefTarget ref_target; + LinkedList1 provides_list; + LinkedList1 depends_list; +}; + +struct scope_instance { + NCDModuleInst *i; + struct scope *scope; +}; + +struct provide { + NCDModuleInst *i; + struct scope *scope; + NCDValRef name; + LinkedList1Node provides_list_node; + LinkedList1 depends_list; + int dying; +}; + +struct depend { + NCDModuleInst *i; + struct scope *scope; + NCDValRef names; + LinkedList1Node depends_list_node; + struct provide *provide; + LinkedList1Node provide_depends_list_node; + int provide_collapsing; +}; + +static struct provide * find_provide (struct scope *o, NCDValRef name) +{ + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->provides_list); ln; ln = LinkedList1Node_Next(ln)) { + struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node); + ASSERT(provide->scope == o) + if (NCDVal_Compare(provide->name, name) == 0) { + return provide; + } + } + + return NULL; +} + +static struct provide * depend_find_best_provide (struct depend *o) +{ + size_t count = NCDVal_ListCount(o->names); + + for (size_t j = 0; j < count; j++) { + NCDValRef name = NCDVal_ListGet(o->names, j); + struct provide *provide = find_provide(o->scope, name); + if (provide && !provide->dying) { + return provide; + } + } + + return NULL; +} + +static void depend_update (struct depend *o) +{ + // if we're collapsing, do nothing + if (o->provide && o->provide_collapsing) { + return; + } + + // find best provide + struct provide *best_provide = depend_find_best_provide(o); + ASSERT(!best_provide || !best_provide->dying) + + // has anything changed? + if (best_provide == o->provide) { + return; + } + + if (o->provide) { + // set collapsing + o->provide_collapsing = 1; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + // insert to provide's list + LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node); + + // set not collapsing + o->provide_collapsing = 0; + + // set provide + o->provide = best_provide; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } +} + +static void scope_ref_target_func_release (BRefTarget *ref_target) +{ + struct scope *o = UPPER_OBJECT(ref_target, struct scope, ref_target); + ASSERT(LinkedList1_IsEmpty(&o->provides_list)) + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + BFree(o); +} + +static void scope_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct scope_instance *o = vo; + o->i = i; + + // pass scope instance pointer to methods not NCDModuleInst pointer + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // allocate scope + o->scope = BAlloc(sizeof(*o->scope)); + if (!o->scope) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + // init reference target + BRefTarget_Init(&o->scope->ref_target, scope_ref_target_func_release); + + // init provide and depend lists + LinkedList1_Init(&o->scope->provides_list); + LinkedList1_Init(&o->scope->depends_list); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void scope_func_die (void *vo) +{ + struct scope_instance *o = vo; + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct provide *o = vo; + o->i = i; + o->scope = ((struct scope_instance *)params->method_user)->scope; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // remember name + o->name = name_arg; + + // check for existing provide with this name + if (find_provide(o->scope, o->name)) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // grab scope reference + if (!BRefTarget_Ref(&o->scope->ref_target)) { + ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail0; + } + + // insert to provides list + LinkedList1_Append(&o->scope->provides_list, &o->provides_list_node); + + // init depends list + LinkedList1_Init(&o->depends_list); + + // set not dying + o->dying = 0; + + // signal up. + // This comes above the loop which follows, so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // update depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->scope->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node); + depend_update(depend); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + // remove from provides list + LinkedList1_Remove(&o->scope->provides_list, &o->provides_list_node); + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(!o->dying) + + // if we have no depends, die immediately + if (LinkedList1_IsEmpty(&o->depends_list)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // start collapsing our depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node); + ASSERT(depend->provide == o) + + // update depend to make sure it is collapsing + depend_update(depend); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct depend *o = vo; + o->i = i; + o->scope = ((struct scope_instance *)params->method_user)->scope; + + // read arguments + NCDValRef names_arg; + if (!NCDVal_ListRead(params->args, 1, &names_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(names_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // remember names + o->names = names_arg; + + // grab scope reference + if (!BRefTarget_Ref(&o->scope->ref_target)) { + ModuleLog(o->i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail0; + } + + // insert to depends list + LinkedList1_Append(&o->scope->depends_list, &o->depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + + if (o->provide) { + // remove from provide's list + LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node); + + // if provide is dying and is empty, let it die + if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) { + provide_free(o->provide); + } + } + + // remove from depends list + LinkedList1_Remove(&o->scope->depends_list, &o->depends_list_node); + + // release scope reference + BRefTarget_Deref(&o->scope->ref_target); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + + if (!(o->provide && o->provide_collapsing)) { + return; + } + + // save provide + struct provide *provide = o->provide; + + // remove from provide's list + LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + + // if provide is dying and is empty, let it die. + // This comes after depend_update so that the side effects of the + // provide dying have priority over rebinding the depend. + if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) { + provide_free(provide); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + + if (!o->provide) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "depend_scope", + .func_new2 = scope_func_new, + .func_die = scope_func_die, + .alloc_size = sizeof(struct scope_instance) + }, { + .type = "depend_scope::provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "depend_scope::depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_depend_scope = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/dynamic_depend.c b/external/badvpn_dns/ncd/modules/dynamic_depend.c new file mode 100644 index 00000000..1fc747a2 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/dynamic_depend.c @@ -0,0 +1,548 @@ +/** + * @file dynamic_depend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dynamic dependencies module. + * + * Synopsis: dynamic_provide(string name, order_value) + * Synopsis: dynamic_depend(string name) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide; + +struct name_string { + char *data; + size_t len; +}; + +struct name { + struct global *g; + struct name_string name; + BAVLNode names_tree_node; + BAVL provides_tree; + LinkedList0 waiting_depends_list; + struct provide *cur_p; + LinkedList0 cur_bound_depends_list; + int cur_resetting; +}; + +struct provide { + NCDModuleInst *i; + struct name *n; + NCDValRef order_value; + BAVLNode provides_tree_node; + int dying; +}; + +struct depend { + NCDModuleInst *i; + struct name *n; + int is_bound; + LinkedList0Node depends_list_node; +}; + +struct global { + BAVL names_tree; +}; + +static void provide_free (struct provide *o); +static void depend_free (struct depend *o); + +static int name_string_comparator (void *user, void *vv1, void *vv2) +{ + struct name_string *v1 = vv1; + struct name_string *v2 = vv2; + + size_t min_len = (v1->len < v2->len ? v1->len : v2->len); + + int cmp = memcmp(v1->data, v2->data, min_len); + if (cmp) { + return B_COMPARE(cmp, 0); + } + + return B_COMPARE(v1->len, v2->len); +} + +static int val_comparator (void *user, NCDValRef *v1, NCDValRef *v2) +{ + return NCDVal_Compare(*v1, *v2); +} + +static struct name * find_name (struct global *g, const char *name, size_t name_len) +{ + struct name_string ns = {(char *)name, name_len}; + BAVLNode *tn = BAVL_LookupExact(&g->names_tree, &ns); + if (!tn) { + return NULL; + } + + struct name *n = UPPER_OBJECT(tn, struct name, names_tree_node); + ASSERT(n->name.len == name_len) + ASSERT(!memcmp(n->name.data, name, name_len)) + + return n; +} + +static struct name * name_init (NCDModuleInst *i, struct global *g, const char *name, size_t name_len) +{ + ASSERT(!find_name(g, name, name_len)) + + // allocate structure + struct name *o = malloc(sizeof(*o)); + if (!o) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set global state + o->g = g; + + // copy name + if (!(o->name.data = b_strdup_bin(name, name_len))) { + ModuleLog(i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + o->name.len = name_len; + + // insert to names tree + ASSERT_EXECUTE(BAVL_Insert(&g->names_tree, &o->names_tree_node, NULL)) + + // init provides tree + BAVL_Init(&o->provides_tree, OFFSET_DIFF(struct provide, order_value, provides_tree_node), (BAVL_comparator)val_comparator, NULL); + + // init waiting depends list + LinkedList0_Init(&o->waiting_depends_list); + + // set no current provide + o->cur_p = NULL; + + return o; + +fail1: + free(o); +fail0: + return NULL; +} + +static void name_free (struct name *o) +{ + ASSERT(BAVL_IsEmpty(&o->provides_tree)) + ASSERT(LinkedList0_IsEmpty(&o->waiting_depends_list)) + ASSERT(!o->cur_p) + + // remove from names tree + BAVL_Remove(&o->g->names_tree, &o->names_tree_node); + + // free name + free(o->name.data); + + // free structure + free(o); +} + +static struct provide * name_get_first_provide (struct name *o) +{ + BAVLNode *tn = BAVL_GetFirst(&o->provides_tree); + if (!tn) { + return NULL; + } + + struct provide *p = UPPER_OBJECT(tn, struct provide, provides_tree_node); + ASSERT(p->n == o) + + return p; +} + +static void name_new_current (struct name *o) +{ + ASSERT(!o->cur_p) + ASSERT(!BAVL_IsEmpty(&o->provides_tree)) + + // set current provide + o->cur_p = name_get_first_provide(o); + + // init bound depends list + LinkedList0_Init(&o->cur_bound_depends_list); + + // set not resetting + o->cur_resetting = 0; + + // bind waiting depends + while (!LinkedList0_IsEmpty(&o->waiting_depends_list)) { + struct depend *d = UPPER_OBJECT(LinkedList0_GetFirst(&o->waiting_depends_list), struct depend, depends_list_node); + ASSERT(d->n == o) + ASSERT(!d->is_bound) + + // remove from waiting depends list + LinkedList0_Remove(&o->waiting_depends_list, &d->depends_list_node); + + // set bound + d->is_bound = 1; + + // add to bound depends list + LinkedList0_Prepend(&o->cur_bound_depends_list, &d->depends_list_node); + + // signal up + NCDModuleInst_Backend_Up(d->i); + } +} + +static void name_free_if_unused (struct name *o) +{ + if (BAVL_IsEmpty(&o->provides_tree) && LinkedList0_IsEmpty(&o->waiting_depends_list)) { + name_free(o); + } +} + +static void name_continue_resetting (struct name *o) +{ + ASSERT(o->cur_p) + ASSERT(o->cur_resetting) + + // still have bound depends? + if (!LinkedList0_IsEmpty(&o->cur_bound_depends_list)) { + return; + } + + struct provide *old_p = o->cur_p; + + // set no current provide + o->cur_p = NULL; + + // free old current provide if it's dying + if (old_p->dying) { + provide_free(old_p); + } + + if (!BAVL_IsEmpty(&o->provides_tree)) { + // get new current provide + name_new_current(o); + } else { + // free name if unused + name_free_if_unused(o); + } +} + +static void name_start_resetting (struct name *o) +{ + ASSERT(o->cur_p) + ASSERT(!o->cur_resetting) + + // set resetting + o->cur_resetting = 1; + + // signal bound depends down + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->cur_bound_depends_list); ln; ln = LinkedList0Node_Next(ln)) { + struct depend *d = UPPER_OBJECT(ln, struct depend, depends_list_node); + ASSERT(d->n == o) + ASSERT(d->is_bound) + NCDModuleInst_Backend_Down(d->i); + } + + // if there were no bound depends, continue right away + name_continue_resetting(o); +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init names tree + BAVL_Init(&g->names_tree, OFFSET_DIFF(struct name, name, names_tree_node), name_string_comparator, NULL); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(BAVL_IsEmpty(&g->names_tree)) + + // free global state structure + BFree(g); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 2, &name_arg, &o->order_value)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *name_str = NCDVal_StringData(name_arg); + size_t name_len = NCDVal_StringLength(name_arg); + + // find name, create new if needed + struct name *n = find_name(g, name_str, name_len); + if (!n && !(n = name_init(i, g, name_str, name_len))) { + goto fail0; + } + + // set name + o->n = n; + + // check for order value conflict + if (BAVL_LookupExact(&n->provides_tree, &o->order_value)) { + ModuleLog(i, BLOG_ERROR, "order value already exists"); + goto fail0; + } + + // add to name's provides tree + ASSERT_EXECUTE(BAVL_Insert(&n->provides_tree, &o->provides_tree_node, NULL)) + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + + // should this be the current provide? + if (o == name_get_first_provide(n)) { + if (!n->cur_p) { + name_new_current(n); + } + else if (!n->cur_resetting) { + name_start_resetting(n); + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + struct name *n = o->n; + ASSERT(o->dying) + ASSERT(o != n->cur_p) + + // remove from name's provides tree + BAVL_Remove(&n->provides_tree, &o->provides_tree_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + struct name *n = o->n; + ASSERT(!o->dying) + + // set dying + o->dying = 1; + + // if this is not the current provide, die right away + if (o != n->cur_p) { + // free provide + provide_free(o); + + // free name if unused + name_free_if_unused(n); + return; + } + + ASSERT(!n->cur_resetting) + + // start resetting + name_start_resetting(n); +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *name_str = NCDVal_StringData(name_arg); + size_t name_len = NCDVal_StringLength(name_arg); + + // find name, create new if needed + struct name *n = find_name(g, name_str, name_len); + if (!n && !(n = name_init(i, g, name_str, name_len))) { + goto fail0; + } + + // set name + o->n = n; + + if (n->cur_p && !n->cur_resetting) { + // set bound + o->is_bound = 1; + + // add to bound depends list + LinkedList0_Prepend(&n->cur_bound_depends_list, &o->depends_list_node); + + // signal up + NCDModuleInst_Backend_Up(i); + } else { + // set not bound + o->is_bound = 0; + + // add to waiting depends list + LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + struct name *n = o->n; + + if (o->is_bound) { + ASSERT(n->cur_p) + + // remove from bound depends list + LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node); + + // continue resetting + if (n->cur_resetting) { + name_continue_resetting(n); + } + } else { + // remove from waiting depends list + LinkedList0_Remove(&n->waiting_depends_list, &o->depends_list_node); + + // free name if unused + name_free_if_unused(n); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + struct name *n = o->n; + ASSERT(!o->is_bound || n->cur_p) + + if (!(o->is_bound && n->cur_resetting)) { + return; + } + + // remove from bound depends list + LinkedList0_Remove(&n->cur_bound_depends_list, &o->depends_list_node); + + // set not bound + o->is_bound = 0; + + // add to waiting depends list + LinkedList0_Prepend(&n->waiting_depends_list, &o->depends_list_node); + + // continue resetting + name_continue_resetting(n); +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + struct name *n = o->n; + ASSERT(!o->is_bound || n->cur_p) + + if (!o->is_bound) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(n->cur_p->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "dynamic_provide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "dynamic_depend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_dynamic_depend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/event_template.c b/external/badvpn_dns/ncd/modules/event_template.c new file mode 100644 index 00000000..2fcab9ee --- /dev/null +++ b/external/badvpn_dns/ncd/modules/event_template.c @@ -0,0 +1,184 @@ +/** + * @file event_template.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#define TemplateLog(o, ...) NCDModuleInst_Backend_Log((o)->i, (o)->blog_channel, __VA_ARGS__) + +static void enable_event (event_template *o) +{ + ASSERT(!LinkedList1_IsEmpty(&o->events_list)) + ASSERT(!o->enabled) + + // get event + struct event_template_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->events_list), struct event_template_event, events_list_node); + + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // grab enabled map + o->enabled_map = e->map; + + // append to free list + LinkedList1_Append(&o->free_list, &e->events_list_node); + + // set enabled + o->enabled = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +void event_template_new (event_template *o, NCDModuleInst *i, int blog_channel, int maxevents, void *user, + event_template_func_free func_free) +{ + ASSERT(maxevents > 0) + + // init arguments + o->i = i; + o->blog_channel = blog_channel; + o->user = user; + o->func_free = func_free; + + // allocate events array + if (!(o->events = BAllocArray(maxevents, sizeof(o->events[0])))) { + TemplateLog(o, BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init events lists + LinkedList1_Init(&o->events_list); + LinkedList1_Init(&o->free_list); + for (int i = 0; i < maxevents; i++) { + LinkedList1_Append(&o->free_list, &o->events[i].events_list_node); + } + + // set not enabled + o->enabled = 0; + + return; + +fail0: + o->func_free(o->user, 1); + return; +} + +void event_template_die (event_template *o) +{ + // free enabled map + if (o->enabled) { + BStringMap_Free(&o->enabled_map); + } + + // free event maps + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->events_list); + while (list_node) { + struct event_template_event *e = UPPER_OBJECT(list_node, struct event_template_event, events_list_node); + BStringMap_Free(&e->map); + list_node = LinkedList1Node_Next(list_node); + } + + // free events array + BFree(o->events); + + o->func_free(o->user, 0); + return; +} + +int event_template_getvar (event_template *o, const char *name, NCDValMem *mem, NCDValRef *out) +{ + ASSERT(o->enabled) + ASSERT(name) + + const char *val = BStringMap_Get(&o->enabled_map, name); + if (!val) { + return 0; + } + + *out = NCDVal_NewString(mem, val); + return 1; +} + +void event_template_queue (event_template *o, BStringMap map, int *out_was_empty) +{ + ASSERT(!LinkedList1_IsEmpty(&o->free_list)) + + // get event + struct event_template_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->free_list), struct event_template_event, events_list_node); + + // remove from free list + LinkedList1_Remove(&o->free_list, &e->events_list_node); + + // set map + e->map = map; + + // insert to events list + LinkedList1_Append(&o->events_list, &e->events_list_node); + + // enable if not already + if (!o->enabled) { + enable_event(o); + *out_was_empty = 1; + } else { + *out_was_empty = 0; + } +} + +void event_template_dequeue (event_template *o, int *out_is_empty) +{ + ASSERT(o->enabled) + + // free enabled map + BStringMap_Free(&o->enabled_map); + + // set not enabled + o->enabled = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + + // enable if there are more events + if (!LinkedList1_IsEmpty(&o->events_list)) { + enable_event(o); + *out_is_empty = 0; + } else { + *out_is_empty = 1; + } +} + +int event_template_is_enabled (event_template *o) +{ + return o->enabled; +} diff --git a/external/badvpn_dns/ncd/modules/event_template.h b/external/badvpn_dns/ncd/modules/event_template.h new file mode 100644 index 00000000..a3a5ea86 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/event_template.h @@ -0,0 +1,64 @@ +/** + * @file event_template.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_MODULES_EVENT_TEMPLATE_H +#define BADVPN_NCD_MODULES_EVENT_TEMPLATE_H + +#include +#include +#include + +typedef void (*event_template_func_free) (void *user, int is_error); + +typedef struct { + NCDModuleInst *i; + int blog_channel; + void *user; + event_template_func_free func_free; + struct event_template_event *events; + LinkedList1 events_list; + LinkedList1 free_list; + int enabled; + BStringMap enabled_map; +} event_template; + +struct event_template_event { + BStringMap map; + LinkedList1Node events_list_node; +}; + +void event_template_new (event_template *o, NCDModuleInst *i, int blog_channel, int maxevents, void *user, + event_template_func_free func_free); +void event_template_die (event_template *o); +int event_template_getvar (event_template *o, const char *name, NCDValMem *mem, NCDValRef *out); +void event_template_queue (event_template *o, BStringMap map, int *out_was_empty); +void event_template_dequeue (event_template *o, int *out_is_empty); +int event_template_is_enabled (event_template *o); + +#endif diff --git a/external/badvpn_dns/ncd/modules/exit.c b/external/badvpn_dns/ncd/modules/exit.c new file mode 100644 index 00000000..d611e2ef --- /dev/null +++ b/external/badvpn_dns/ncd/modules/exit.c @@ -0,0 +1,91 @@ +/** + * @file exit.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * exit(string exit_code) + * + * Description: + * Initiates termination of the interpreter. Calling exit() multiple times will override + * the previously set exit code. When the interpreter receives a signal, it reacts + * equivalently to calling exit(1) (possibly overriding your error code). + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef exit_code_arg; + if (!NCDVal_ListRead(params->args, 1, &exit_code_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(exit_code_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse exit code + uintmax_t exit_code; + if (!ncd_read_uintmax(exit_code_arg, &exit_code) || exit_code >= INT_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong exit code value"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + + // initiate exit (before up!) + NCDModuleInst_Backend_InterpExit(i, exit_code); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "exit", + .func_new2 = func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_exit = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/explode.c b/external/badvpn_dns/ncd/modules/explode.c new file mode 100644 index 00000000..08425d5d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/explode.c @@ -0,0 +1,232 @@ +/** + * @file explode.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * explode(string delimiter, string input [, string limit]) + * + * Description: + * Splits the string 'input' into a list of components. The first component + * is the part of 'input' until the first occurence of 'delimiter', if any. + * If 'delimiter' was found, the remaining components are defined recursively + * via the same procedure, starting with the part of 'input' after the first + * substring. + * 'delimiter' must be nonempty. + * + * Variables: + * list (empty) - the components of 'input', determined based on 'delimiter' + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + struct ExpArray arr; + size_t num; +}; + +struct substring { + char *data; + size_t len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef delimiter_arg; + NCDValRef input_arg; + NCDValRef limit_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &delimiter_arg, &input_arg) && !NCDVal_ListRead(params->args, 3, &delimiter_arg, &input_arg, &limit_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(delimiter_arg) || !NCDVal_IsString(input_arg) || (!NCDVal_IsInvalid(limit_arg) && !NCDVal_IsString(limit_arg))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t limit = SIZE_MAX; + if (!NCDVal_IsInvalid(limit_arg)) { + uintmax_t n; + if (!ncd_read_uintmax(limit_arg, &n) || n == 0) { + ModuleLog(i, BLOG_ERROR, "bad limit argument"); + goto fail0; + } + n--; + limit = (n <= SIZE_MAX ? n : SIZE_MAX); + } + + const char *del_data = NCDVal_StringData(delimiter_arg); + size_t del_len = NCDVal_StringLength(delimiter_arg); + + if (del_len == 0) { + ModuleLog(i, BLOG_ERROR, "delimiter must be nonempty"); + goto fail0; + } + + size_t *table = BAllocArray(del_len, sizeof(table[0])); + if (!table) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail0; + } + + build_substring_backtrack_table(del_data, del_len, table); + + if (!ExpArray_init(&o->arr, sizeof(struct substring), 8)) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail1; + } + o->num = 0; + + const char *data = NCDVal_StringData(input_arg); + size_t len = NCDVal_StringLength(input_arg); + + while (1) { + size_t start; + int is_end = 0; + if (limit == 0 || !find_substring(data, len, del_data, del_len, table, &start)) { + start = len; + is_end = 1; + } + + if (!ExpArray_resize(&o->arr, o->num + 1)) { + ModuleLog(i, BLOG_ERROR, "ExpArray_init failed"); + goto fail2; + } + + struct substring *elem = &((struct substring *)o->arr.v)[o->num]; + + if (!(elem->data = BAlloc(start))) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail2; + } + + memcpy(elem->data, data, start); + elem->len = start; + o->num++; + + if (is_end) { + break; + } + + data += start + del_len; + len -= start + del_len; + limit--; + } + + BFree(table); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail2: + while (o->num-- > 0) { + BFree(((struct substring *)o->arr.v)[o->num].data); + } + free(o->arr.v); +fail1: + BFree(table); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + while (o->num-- > 0) { + BFree(((struct substring *)o->arr.v)[o->num].data); + } + free(o->arr.v); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewList(mem, o->num); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + for (size_t j = 0; j < o->num; j++) { + struct substring *elem = &((struct substring *)o->arr.v)[j]; + NCDValRef str = NCDVal_NewStringBin(mem, (uint8_t *)elem->data, elem->len); + if (NCDVal_IsInvalid(str)) { + goto fail; + } + if (!NCDVal_ListAppend(*out, str)) { + goto fail; + } + } + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "explode", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_explode = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/file.c b/external/badvpn_dns/ncd/modules/file.c new file mode 100644 index 00000000..bda93ea6 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/file.c @@ -0,0 +1,350 @@ +/** + * @file file.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * File I/O module. + * + * Synopsis: + * file_read(string filename) + * + * Variables: + * string (empty) - file contents + * + * Description: + * Reads the contents of a file. Reports an error if something goes wrong. + * WARNING: this uses fopen/fread/fclose, blocking the entire interpreter while + * the file is being read. For this reason, you should only use this + * to read small local files which will be read quickly, and especially + * not files on network mounts. + * + * Synopsis: + * file_write(string filename, string contents) + * + * Description: + * Writes a file, possibly overwriting an existing one. Reports an error if something + * goes wrong. + * WARNING: this is not an atomic operation; other programs may see the file in an + * inconsistent state while it is being written. Similarly, if writing + * fails, the file may remain in an inconsistent state indefinitely. + * If this is a problem, you should write the new contents to a temporary + * file and rename this temporary file to the live file. + * WARNING: this uses fopen/fwrite/fclose, blocking the entire interpreter while + * the file is being written. For this reason, you should only use this + * to write small local files which will be written quickly, and especially + * not files on network mounts. + * + * Synopsis: + * file_stat(string filename) + * file_lstat(string filename) + * + * Description: + * Retrieves information about a file. + * file_stat() follows symlinks; file_lstat() does not and allows retrieving information + * about a symlink. + * WARNING: this blocks the interpreter + * + * Variables: + * succeeded - whether the stat operation succeeded (true/false). If false, all other + * variables obtain the value "failed". + * type - file, dir, chr, blk, fifo, link, socket, other, failed + * size - size of the file, or failed + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct read_instance { + NCDModuleInst *i; + uint8_t *file_data; + size_t file_len; +}; + +struct stat_instance { + NCDModuleInst *i; + int succeeded; + struct stat result; +}; + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef filename_arg; + if (!NCDVal_ListRead(params->args, 1, &filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get null terminated name + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // read file + int res = read_file(filename_nts.data, &o->file_data, &o->file_len); + NCDValNullTermString_Free(&filename_nts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "failed to read file"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // free data + free(o->file_data); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewStringBin(mem, o->file_data, o->file_len); + return 1; + } + + return 0; +} + +static void write_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef filename_arg; + NCDValRef contents_arg; + if (!NCDVal_ListRead(params->args, 2, &filename_arg, &contents_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg) || !NCDVal_IsString(contents_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get null terminated name + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // write file + b_cstring contents_cstr = NCDVal_StringCstring(contents_arg); + int res = write_file_cstring(filename_nts.data, contents_cstr, 0, contents_cstr.length); + NCDValNullTermString_Free(&filename_nts); + if (!res) { + ModuleLog(i, BLOG_ERROR, "failed to write file"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stat_func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_lstat) +{ + struct stat_instance *o = vo; + o->i = i; + + NCDValRef filename_arg; + if (!NCDVal_ListRead(params->args, 1, &filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(filename_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = 0; + + if (!NCDVal_IsStringNoNulls(filename_arg)) { + goto out; + } + + // null terminate filename + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + int res; + if (is_lstat) { + res = lstat(filename_nts.data, &o->result); + } else { + res = stat(filename_nts.data, &o->result); + } + NCDValNullTermString_Free(&filename_nts); + + if (res < 0) { + goto out; + } + + o->succeeded = 1; + +out: + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stat_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + stat_func_new_common(vo, i, params, 0); +} + +static void lstat_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + stat_func_new_common(vo, i, params, 1); +} + +static int stat_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct stat_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (name == NCD_STRING_TYPE) { + const char *str; + + if (!o->succeeded) { + str = "failed"; + } else if (S_ISREG(o->result.st_mode)) { + str = "file"; + } else if (S_ISDIR(o->result.st_mode)) { + str = "dir"; + } else if (S_ISCHR(o->result.st_mode)) { + str = "chr"; + } else if (S_ISBLK(o->result.st_mode)) { + str = "blk"; + } else if (S_ISFIFO(o->result.st_mode)) { + str = "fifo"; + } else if (S_ISLNK(o->result.st_mode)) { + str = "link"; + } else if (S_ISSOCK(o->result.st_mode)) { + str = "socket"; + } else { + str = "other"; + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (name == NCD_STRING_SIZE) { + char str[50]; + if (!o->succeeded) { + strcpy(str, "failed"); + } else { + generate_decimal_repr_string((uintmax_t)o->result.st_size, str); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "file_read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar2, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_write", + .func_new2 = write_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_stat", + .func_new2 = stat_func_new, + .func_getvar2 = stat_func_getvar2, + .alloc_size = sizeof(struct stat_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_lstat", + .func_new2 = lstat_func_new, + .func_getvar2 = stat_func_getvar2, + .alloc_size = sizeof(struct stat_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_file = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/file_open.c b/external/badvpn_dns/ncd/modules/file_open.c new file mode 100644 index 00000000..d0a14c34 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/file_open.c @@ -0,0 +1,585 @@ +/** + * @file file_open.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * file_open(string filename, string mode [, map options]) + * + * Variables: + * string is_error - "true" if the file_open object is in error state, "false" + * otherwise + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Description: + * Opens a file for subsequent reading or writing. The 'mode' argument must + * be one of: "r", "w", "a", "r+", "w+", "a+"; it corresponds to the mode string + * that will be passed to the fopen() function. + * When the file_open() statement goes up, the error state is set depending on + * whether opening succeeded or failed. The 'is_error' variable should be used + * to check the error state. + * If an error occurs afterward within read(), write() or seek(), the error state + * is set, and the file_open() statement is toggled down and back up. This way, + * the same piece of user code can handle all file errors. + * + * Synopsis: + * file_open::read() + * + * Variables: + * string (empty) - the data which was read, or an empty string if EOF was reached + * string not_eof - "false" if EOF was reached, "true" if not + * + * Description: + * Reads data from an opened file. The file must not be in error state. + * If reading fails, this statement will never go up, the error state of the + * file_open() statement will be set, and the file_open() statement will trigger + * backtracking (go down and up). + * + * Synopsis: + * file_open::write(string data) + * + * Description: + * Writes data to an opened file. The file must not be in error state. + * If writing fails, this statement will never go up, the error state of the + * file_open() statement will be set, and the file_open() statement will trigger + * backtracking (go down and up). + * + * Synopsis: + * file_open::seek(string position, string whence) + * + * Description: + * Sets the file position indicator. The 'position' argument must be a possibly + * negative decimal number, and is interpreted relative to 'whence'. Here, 'whence' + * may be one of: + * - "set", meaning beginning of file, + * - "cur", meaning the current position, and + * - "end", meaning the end of file. + * Errors are handled as in read() and write(). Note that if the position argument + * is too small or too large to convert to off_t, this is not a seek error, and only + * the seek command will fail. + * + * Synopsis: + * file_open::close() + * + * Description: + * Closes the file. The file must not be in error state. + * Errors are handled as handled as in read() and write(), i.e. the process is + * backtracked to file_open() with the error state set. + * On success, the error state of the file is set (but without backtracking), and + * the close() statement goes up . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define READ_BUF_SIZE 8192 + +struct open_instance { + NCDModuleInst *i; + FILE *fh; + NCDBufStore store; +}; + +struct read_instance { + NCDModuleInst *i; + NCDBuf *buf; + size_t length; +}; + +static int parse_mode (b_cstring cstr, char *out) +{ + size_t pos = 0; + size_t left = cstr.length; + + if (left == 0) { + return 0; + } + switch (b_cstring_at(cstr, pos)) { + case 'r': + case 'w': + case 'a': + *out++ = b_cstring_at(cstr, pos); + pos++; + left--; + break; + default: + return 0; + } + + if (left == 0) { + goto finish; + } + switch (b_cstring_at(cstr, pos)) { + case '+': + *out++ = b_cstring_at(cstr, pos); + pos++; + left--; + break; + default: + return 0; + } + + if (left == 0) { + goto finish; + } + + return 0; + +finish: + *out = '\0'; + return 1; +} + +static void trigger_error (struct open_instance *o) +{ + if (o->fh) { + // close file + if (fclose(o->fh) != 0) { + ModuleLog(o->i, BLOG_ERROR, "fclose failed"); + } + + // set no file, indicating error + o->fh = NULL; + } + + // go down and up + NCDModuleInst_Backend_Down(o->i); + NCDModuleInst_Backend_Up(o->i); +} + +static void open_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct open_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef filename_arg; + NCDValRef mode_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &filename_arg, &mode_arg) && + !NCDVal_ListRead(params->args, 3, &filename_arg, &mode_arg, &options_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(filename_arg) || !NCDVal_IsString(mode_arg) || + (!NCDVal_IsInvalid(options_arg) && !NCDVal_IsMap(options_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // check mode + char mode[5]; + if (!parse_mode(NCDVal_StringCstring(mode_arg), mode)) { + ModuleLog(o->i, BLOG_ERROR, "wrong mode"); + goto fail0; + } + + size_t read_size_opt = READ_BUF_SIZE; + + // parse options + if (!NCDVal_IsInvalid(options_arg)) { + int num_recognized = 0; + NCDValRef value; + + if (!NCDVal_IsInvalid(value = NCDVal_MapGetValue(options_arg, "read_size"))) { + uintmax_t read_size; + if (!NCDVal_IsString(value) || !ncd_read_uintmax(value, &read_size) || read_size > SIZE_MAX || read_size == 0) { + ModuleLog(o->i, BLOG_ERROR, "wrong read_size"); + goto fail0; + } + num_recognized++; + read_size_opt = read_size; + } + + if (NCDVal_MapCount(options_arg) > num_recognized) { + ModuleLog(o->i, BLOG_ERROR, "unrecognized options present"); + goto fail0; + } + } + + // init store + NCDBufStore_Init(&o->store, read_size_opt); + + // null terminate filename + NCDValNullTermString filename_nts; + if (!NCDVal_StringNullTerminate(filename_arg, &filename_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + + // open file + o->fh = fopen(filename_nts.data, mode); + NCDValNullTermString_Free(&filename_nts); + if (!o->fh) { + ModuleLog(o->i, BLOG_ERROR, "fopen failed"); + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDBufStore_Free(&o->store); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void open_func_die (void *vo) +{ + struct open_instance *o = vo; + + // close file + if (o->fh) { + if (fclose(o->fh) != 0) { + ModuleLog(o->i, BLOG_ERROR, "fclose failed"); + } + } + + // free store + NCDBufStore_Free(&o->store); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int open_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct open_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + *out = ncd_make_boolean(mem, !o->fh, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(o->i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&open_inst->store); + if (!o->buf) { + ModuleLog(o->i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // starting with empty buffer + char *data = NCDBuf_Data(o->buf); + size_t buf_size = NCDBufStore_BufSize(&open_inst->store); + o->length = 0; + + while (o->length < buf_size) { + // read + size_t readed = fread(data + o->length, 1, buf_size - o->length, open_inst->fh); + if (readed == 0) { + break; + } + ASSERT(readed <= buf_size - o->length) + + // increment length + o->length += readed; + } + + // if we couldn't read anything due to an error, trigger + // error in the open instance, and don't go up + if (o->length == 0 && !feof(open_inst->fh)) { + ModuleLog(o->i, BLOG_ERROR, "fread failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // release buffer + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->length, NCDBuf_RefTarget(o->buf)); + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + *out = ncd_make_boolean(mem, (o->length != 0), o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // write all the data + b_cstring data_cstr = NCDVal_StringCstring(data_arg); + B_CSTRING_LOOP(data_cstr, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + size_t written = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, open_inst->fh); + if (written == 0) { + ModuleLog(i, BLOG_ERROR, "fwrite failed"); + trigger_error(open_inst); + return; + } + ASSERT(written <= chunk_length - chunk_pos) + chunk_pos += written; + } + }) + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void seek_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef position_arg; + NCDValRef whence_arg; + if (!NCDVal_ListRead(params->args, 2, &position_arg, &whence_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(position_arg) || !NCDVal_IsString(whence_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse position + int position_sign; + uintmax_t position_mag; + b_cstring position_cstr = NCDVal_StringCstring(position_arg); + if (!parse_signmag_integer_cstr(position_cstr, 0, position_cstr.length, &position_sign, &position_mag)) { + ModuleLog(i, BLOG_ERROR, "wrong position"); + goto fail0; + } + + // parse whence + int whence; + if (NCDVal_StringEquals(whence_arg, "set")) { + whence = SEEK_SET; + } + else if (NCDVal_StringEquals(whence_arg, "cur")) { + whence = SEEK_CUR; + } + else if (NCDVal_StringEquals(whence_arg, "end")) { + whence = SEEK_END; + } + else { + ModuleLog(i, BLOG_ERROR, "wrong whence"); + goto fail0; + } + + // determine min/max values of off_t (non-portable hack) + off_t off_t_min = (sizeof(off_t) == 8 ? INT64_MIN : INT32_MIN); + off_t off_t_max = (sizeof(off_t) == 8 ? INT64_MAX : INT32_MAX); + + // compute position as off_t + off_t position; + if (position_sign < 0 && position_mag > 0) { + if (position_mag - 1 > -(off_t_min + 1)) { + ModuleLog(i, BLOG_ERROR, "position underflow"); + goto fail0; + } + position = -(off_t)(position_mag - 1) - 1; + } else { + if (position_mag > off_t_max) { + ModuleLog(i, BLOG_ERROR, "position overflow"); + goto fail0; + } + position = position_mag; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // seek + if (fseeko(open_inst->fh, position, whence) < 0) { + ModuleLog(i, BLOG_ERROR, "fseeko failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void close_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get open instance + struct open_instance *open_inst = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure it's not in error + if (!open_inst->fh) { + ModuleLog(i, BLOG_ERROR, "open instance is in error"); + goto fail0; + } + + // close + int res = fclose(open_inst->fh); + open_inst->fh = NULL; + if (res != 0) { + ModuleLog(i, BLOG_ERROR, "fclose failed"); + trigger_error(open_inst); + return; + } + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "file_open", + .func_new2 = open_func_new, + .func_die = open_func_die, + .func_getvar2 = open_func_getvar, + .alloc_size = sizeof(struct open_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::write", + .func_new2 = write_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::seek", + .func_new2 = seek_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "file_open::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_file_open = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/foreach.c b/external/badvpn_dns/ncd/modules/foreach.c new file mode 100644 index 00000000..f0b3effa --- /dev/null +++ b/external/badvpn_dns/ncd/modules/foreach.c @@ -0,0 +1,715 @@ +/** + * @file foreach.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * foreach(list/map collection, string template, list args) + * + * Description: + * Initializes a template process for each element of list, sequentially, + * obeying to the usual execution model of NCD. + * It's equivalent to (except for special variables): + * + * call(template, args); + * ... + * call(template, args); # one call() for every element of list + * + * Template process specials: + * + * _index - (lists only) index of the list element corresponding to the template, + * process, as a decimal string, starting from zero + * _elem - (lists only) element of list corresponding to the template process + * _key - (maps only) key of the current map entry + * _val - (maps only) value of the current map entry + * _caller.X - X as seen from the foreach() statement + * + * Synopsis: + * foreach_emb(list/map collection, string template, string name1 [, string name2]) + * + * Description: + * Foreach for embedded templates; the desugaring process converts Foreach + * clauses into this statement. The called templates have direct access to + * objects as seen from this statement, and also some kind of access to the + * current element of the iteration, depending on the type of collection + * being iterated, and whether 'name2' is provided: + * List and one name: current element is named 'name1'. + * List and both names: current index is named 'name1', current element 'name2'. + * Map and one name: current key is named 'name1'. + * Map and both names: current key is named 'name1', current value 'name2'. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define ISTATE_WORKING 1 +#define ISTATE_UP 2 +#define ISTATE_WAITING 3 +#define ISTATE_TERMINATING 4 + +#define ESTATE_FORGOTTEN 1 +#define ESTATE_DOWN 2 +#define ESTATE_UP 3 +#define ESTATE_WAITING 4 +#define ESTATE_TERMINATING 5 + +struct element; + +struct instance { + NCDModuleInst *i; + NCDValRef template_name; + NCDValRef args; + NCD_string_id_t name1; + NCD_string_id_t name2; + BTimer timer; + struct element *elems; + int type; + int num_elems; + int gp; // good pointer + int ip; // initialized pointer + int state; +}; + +struct element { + struct instance *inst; + union { + struct { + NCDValRef list_elem; + }; + struct { + NCDValRef map_key; + NCDValRef map_val; + }; + }; + NCDModuleProcess process; + int i; + int state; +}; + +static void assert_state (struct instance *o); +static void work (struct instance *o); +static void advance (struct instance *o); +static void timer_handler (struct instance *o); +static void element_process_handler_event (NCDModuleProcess *process, int event); +static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static void instance_free (struct instance *o); + +enum {STRING_INDEX, STRING_ELEM, STRING_KEY, STRING_VAL}; + +static const char *strings[] = { + "_index", "_elem", "_key", "_val", NULL +}; + +static void assert_state (struct instance *o) +{ + ASSERT(o->num_elems >= 0) + ASSERT(o->gp >= 0) + ASSERT(o->ip >= 0) + ASSERT(o->gp <= o->num_elems) + ASSERT(o->ip <= o->num_elems) + ASSERT(o->gp <= o->ip) + +#ifndef NDEBUG + // check GP + for (int i = 0; i < o->gp; i++) { + if (i == o->gp - 1) { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING) + } else { + ASSERT(o->elems[i].state == ESTATE_UP) + } + } + + // check IP + int ip = o->num_elems; + while (ip > 0 && o->elems[ip - 1].state == ESTATE_FORGOTTEN) { + ip--; + } + ASSERT(o->ip == ip) + + // check gap + for (int i = o->gp; i < o->ip; i++) { + if (i == o->ip - 1) { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING || o->elems[i].state == ESTATE_TERMINATING) + } else { + ASSERT(o->elems[i].state == ESTATE_UP || o->elems[i].state == ESTATE_DOWN || + o->elems[i].state == ESTATE_WAITING) + } + } +#endif +} + +static void work (struct instance *o) +{ + assert_state(o); + + // stop timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + if (o->state == ISTATE_WAITING) { + return; + } + + if (o->state == ISTATE_UP && !(o->gp == o->ip && o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP))) { + // signal down + NCDModuleInst_Backend_Down(o->i); + + // set state waiting + o->state = ISTATE_WAITING; + return; + } + + if (o->gp < o->ip) { + // get last element + struct element *le = &o->elems[o->ip - 1]; + ASSERT(le->state != ESTATE_FORGOTTEN) + + // start terminating if not already + if (le->state != ESTATE_TERMINATING) { + // request termination + NCDModuleProcess_Terminate(&le->process); + + // set element state terminating + le->state = ESTATE_TERMINATING; + } + + return; + } + + if (o->state == ISTATE_TERMINATING) { + // finally die + instance_free(o); + return; + } + + if (o->gp == o->num_elems && (o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP)) { + if (o->state == ISTATE_WORKING) { + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = ISTATE_UP; + } + + return; + } + + if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_WAITING) { + // get last element + struct element *le = &o->elems[o->gp - 1]; + + // continue process + NCDModuleProcess_Continue(&le->process); + + // set state down + le->state = ESTATE_DOWN; + return; + } + + if (o->gp > 0 && o->elems[o->gp - 1].state == ESTATE_DOWN) { + return; + } + + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + + advance(o); + return; +} + +static void advance (struct instance *o) +{ + assert_state(o); + ASSERT(o->gp == o->ip) + ASSERT(o->gp < o->num_elems) + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN) + + // get next element + struct element *e = &o->elems[o->gp]; + + // init process + if (!NCDModuleProcess_InitValue(&e->process, o->i, o->template_name, o->args, element_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&e->process, element_process_func_getspecialobj); + + // set element state down + e->state = ESTATE_DOWN; + + // increment GP and IP + o->gp++; + o->ip++; + return; + +fail: + // set timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +static void timer_handler (struct instance *o) +{ + assert_state(o); + ASSERT(o->gp == o->ip) + ASSERT(o->gp < o->num_elems) + ASSERT(o->gp == 0 || o->elems[o->gp - 1].state == ESTATE_UP) + ASSERT(o->elems[o->gp].state == ESTATE_FORGOTTEN) + + advance(o); + return; +} + +static void element_process_handler_event (NCDModuleProcess *process, int event) +{ + struct element *e = UPPER_OBJECT(process, struct element, process); + struct instance *o = e->inst; + assert_state(o); + ASSERT(e->i < o->ip) + ASSERT(e->state != ESTATE_FORGOTTEN) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(e->state == ESTATE_DOWN) + ASSERT(o->gp == o->ip) + ASSERT(o->gp == e->i + 1) + + // set element state up + e->state = ESTATE_UP; + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(e->state == ESTATE_UP) + + // set element state waiting + e->state = ESTATE_WAITING; + + // bump down GP + if (o->gp > e->i + 1) { + o->gp = e->i + 1; + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(e->state == ESTATE_TERMINATING) + ASSERT(o->gp < o->ip) + ASSERT(o->ip == e->i + 1) + + // free process + NCDModuleProcess_Free(&e->process); + + // set element state forgotten + e->state = ESTATE_FORGOTTEN; + + // decrement IP + o->ip--; + } break; + + default: ASSERT(0); + } + + work(o); + return; +} + +static int element_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct element *e = UPPER_OBJECT(process, struct element, process); + struct instance *o = e->inst; + ASSERT(e->state != ESTATE_FORGOTTEN) + + switch (o->type) { + case NCDVAL_LIST: { + NCD_string_id_t index_name = (o->name2 >= 0 ? o->name1 : -1); + NCD_string_id_t elem_name = (o->name2 >= 0 ? o->name2 : o->name1); + + if (index_name >= 0 && name == index_name) { + *out_object = NCDObject_Build(-1, e, element_list_index_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (name == elem_name) { + *out_object = NCDObject_Build(-1, e, element_list_elem_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } break; + case NCDVAL_MAP: { + NCD_string_id_t key_name = o->name1; + NCD_string_id_t val_name = o->name2; + + if (name == key_name) { + *out_object = NCDObject_Build(-1, e, element_map_key_object_func_getvar, NCDObject_no_getobj); + return 1; + } + + if (val_name >= 0 && name == val_name) { + *out_object = NCDObject_Build(-1, e, element_map_val_object_func_getvar, NCDObject_no_getobj); + return 1; + } + } break; + } + + if (NCDVal_IsInvalid(o->args)) { + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); + } + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, e, NCDObject_no_getvar, element_caller_object_func_getobj); + return 1; + } + + return 0; +} + +static int element_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(!NCDVal_IsInvalid(o->args)) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int element_list_index_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_LIST) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = ncd_make_uintmax(mem, e->i); + return 1; +} + +static int element_list_elem_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_LIST) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->list_elem); + return 1; +} + +static int element_map_key_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_MAP) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->map_key); + return 1; +} + +static int element_map_val_object_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct element *e = NCDObject_DataPtr(obj); + struct instance *o = e->inst; + B_USE(o) + ASSERT(e->state != ESTATE_FORGOTTEN) + ASSERT(o->type == NCDVAL_MAP) + + if (name != NCD_STRING_EMPTY) { + return 0; + } + + *out = NCDVal_NewCopy(mem, e->map_val); + return 1; +} + +static void func_new_common (void *vo, NCDModuleInst *i, NCDValRef collection, NCDValRef template_name, NCDValRef args, NCD_string_id_t name1, NCD_string_id_t name2) +{ + ASSERT(!NCDVal_IsInvalid(collection)) + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsInvalid(args) || NCDVal_IsList(args)) + ASSERT(name1 >= 0) + + struct instance *o = vo; + o->i = i; + + o->type = NCDVal_Type(collection); + o->template_name = template_name; + o->args = args; + o->name1 = name1; + o->name2 = name2; + + // init timer + btime_t retry_time = NCDModuleInst_Backend_InterpGetRetryTime(i); + BTimer_Init(&o->timer, retry_time, (BTimer_handler)timer_handler, o); + + size_t num_elems; + NCDValMapElem cur_map_elem; + + switch (o->type) { + case NCDVAL_LIST: { + num_elems = NCDVal_ListCount(collection); + } break; + case NCDVAL_MAP: { + num_elems = NCDVal_MapCount(collection); + cur_map_elem = NCDVal_MapOrderedFirst(collection); + } break; + default: + ModuleLog(i, BLOG_ERROR, "invalid collection type"); + goto fail0; + } + + if (num_elems > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "too many elements"); + goto fail0; + } + o->num_elems = num_elems; + + // allocate elements + if (!(o->elems = BAllocArray(o->num_elems, sizeof(o->elems[0])))) { + ModuleLog(i, BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + for (int j = 0; j < o->num_elems; j++) { + struct element *e = &o->elems[j]; + + // set instance + e->inst = o; + + // set index + e->i = j; + + // set state forgotten + e->state = ESTATE_FORGOTTEN; + + // set values + switch (o->type) { + case NCDVAL_LIST: { + e->list_elem = NCDVal_ListGet(collection, j); + } break; + case NCDVAL_MAP: { + e->map_key = NCDVal_MapElemKey(collection, cur_map_elem); + e->map_val = NCDVal_MapElemVal(collection, cur_map_elem); + cur_map_elem = NCDVal_MapOrderedNext(collection, cur_map_elem); + } break; + } + } + + // set GP and IP zero + o->gp = 0; + o->ip = 0; + + // set state working + o->state = ISTATE_WORKING; + + work(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_foreach (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_collection; + NCDValRef arg_template; + NCDValRef arg_args; + if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template) || !NCDVal_IsList(arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCD_string_id_t name1; + NCD_string_id_t name2; + + switch (NCDVal_Type(arg_collection)) { + case NCDVAL_LIST: { + name1 = ModuleString(i, STRING_INDEX); + name2 = ModuleString(i, STRING_ELEM); + } break; + case NCDVAL_MAP: { + name1 = ModuleString(i, STRING_KEY); + name2 = ModuleString(i, STRING_VAL); + } break; + default: + ModuleLog(i, BLOG_ERROR, "invalid collection type"); + goto fail0; + } + + func_new_common(vo, i, arg_collection, arg_template, arg_args, name1, name2); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_foreach_emb (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_collection; + NCDValRef arg_template; + NCDValRef arg_name1; + NCDValRef arg_name2 = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 3, &arg_collection, &arg_template, &arg_name1) && !NCDVal_ListRead(params->args, 4, &arg_collection, &arg_template, &arg_name1, &arg_name2)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template) || !NCDVal_IsString(arg_name1) || (!NCDVal_IsInvalid(arg_name2) && !NCDVal_IsString(arg_name2))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCD_string_id_t name1 = ncd_get_string_id(arg_name1, i->params->iparams->string_index); + if (name1 < 0) { + ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail0; + } + + NCD_string_id_t name2 = -1; + if (!NCDVal_IsInvalid(arg_name2)) { + name2 = ncd_get_string_id(arg_name2, i->params->iparams->string_index); + if (name2 < 0) { + ModuleLog(i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail0; + } + } + + func_new_common(vo, i, arg_collection, arg_template, NCDVal_NewInvalid(), name1, name2); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(o->gp == 0) + ASSERT(o->ip == 0) + + // free elements + BFree(o->elems); + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + assert_state(o); + ASSERT(o->state != ISTATE_TERMINATING) + + // set GP zero + o->gp = 0; + + // set state terminating + o->state = ISTATE_TERMINATING; + + work(o); + return; +} + +static void func_clean (void *vo) +{ + struct instance *o = vo; + + if (o->state != ISTATE_WAITING) { + return; + } + + // set state working + o->state = ISTATE_WORKING; + + work(o); + return; +} + +static struct NCDModule modules[] = { + { + .type = "foreach", + .func_new2 = func_new_foreach, + .func_die = func_die, + .func_clean = func_clean, + .alloc_size = sizeof(struct instance) + }, { + .type = "foreach_emb", + .func_new2 = func_new_foreach_emb, + .func_die = func_die, + .func_clean = func_clean, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_foreach = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/from_string.c b/external/badvpn_dns/ncd/modules/from_string.c new file mode 100644 index 00000000..3bc446b3 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/from_string.c @@ -0,0 +1,125 @@ +/** + * @file from_string.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * from_string(string str) + * Variables: + * (empty) - str, parsed as a value + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef val; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // parse value string + if (!NCDValParser_Parse(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->mem, &o->val)) { + ModuleLog(i, BLOG_ERROR, "failed to parse"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->val); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "from_string", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_from_string = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/getargs.c b/external/badvpn_dns/ncd/modules/getargs.c new file mode 100644 index 00000000..f620a65a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/getargs.c @@ -0,0 +1,98 @@ +/** + * @file getargs.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * getargs() + * + * Variables: + * (empty) - list of extra command line arguments that were passed to the intrepreter + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + if (!NCDModuleInst_Backend_InterpGetArgs(o->i, mem, out)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleInst_Backend_InterpGetArgs failed"); + return 0; + } + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "getargs", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_getargs = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/getenv.c b/external/badvpn_dns/ncd/modules/getenv.c new file mode 100644 index 00000000..2cb3e914 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/getenv.c @@ -0,0 +1,153 @@ +/** + * @file getenv.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * getenv(string name) + * + * Variables: + * string (empty) - if the environment value exists, its value + * string exists - "true" if the variable exists, "false" if not + */ + +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + char *value; +}; + +enum {STRING_EXISTS}; + +static const char *strings[] = {"exists", NULL}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->value = NULL; + + if (NCDVal_StringHasNulls(name_arg)) { + goto out; + } + + NCDValNullTermString nts; + if (!NCDVal_StringNullTerminate(name_arg, &nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + char *result = getenv(nts.data); + NCDValNullTermString_Free(&nts); + if (!result) { + goto out; + } + + o->value = b_strdup(result); + if (!o->value) { + ModuleLog(i, BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + +out: + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free value + if (o->value) { + BFree(o->value); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY && o->value) { + *out = NCDVal_NewString(mem, o->value); + return 1; + } + + if (name == ModuleString(o->i, STRING_EXISTS)) { + *out = ncd_make_boolean(mem, !!o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "getenv", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_getenv = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/if.c b/external/badvpn_dns/ncd/modules/if.c new file mode 100644 index 00000000..3bbd3629 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/if.c @@ -0,0 +1,103 @@ +/** + * @file if.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Conditional module. + * + * Synopsis: if(string cond) + * Description: on initialization, transitions to UP state if cond equals "true", else + * remains in the DOWN state indefinitely. + * + * Synopsis: ifnot(string cond) + * Description: on initialization, transitions to UP state if cond does not equal "true", else + * remains in the DOWN state indefinitely. + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void new_templ (NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_not) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // compute logical value of argument + int c = ncd_read_boolean(arg); + + // signal up if needed + if ((is_not && !c) || (!is_not && c)) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(i, params, 0); +} + +static void func_new_not (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "if", + .func_new2 = func_new + }, { + .type = "ifnot", + .func_new2 = func_new_not + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_if = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/imperative.c b/external/badvpn_dns/ncd/modules/imperative.c new file mode 100644 index 00000000..f9648885 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/imperative.c @@ -0,0 +1,324 @@ +/** + * @file imperative.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Imperative statement. + * + * Synopsis: + * imperative(string init_template, list init_args, string deinit_template, list deinit_args, string deinit_timeout) + * + * Description: + * Does the following, in order: + * 1. Starts a template process from (init_template, init_args) and waits for it to + * initialize completely. + * 2. Initiates termination of the process and wait for it to terminate. + * 3. Puts the statement UP, then waits for a statement termination request (which may + * already have been received). + * 4. Starts a template process from (deinit_template, deinit_args) and waits for it + * to initialize completely, or for the timeout to elapse. + * 5. Initiates termination of the process and wait for it to terminate. + * 6. Terminates the statement. + * + * If init_template="", steps (1-2) are skipped. + * If deinit_template="", steps (4-5) are skipped. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_INIT_WORKING 1 +#define STATE_INIT_CLEANING 2 +#define STATE_UP 3 +#define STATE_DEINIT_WORKING 4 +#define STATE_DEINIT_CLEANING 5 + +struct instance { + NCDModuleInst *i; + NCDValRef deinit_template; + NCDValRef deinit_args; + BTimer deinit_timer; + NCDModuleProcess process; + int state; + int dying; +}; + +static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler); +static void go_deinit (struct instance *o); +static void init_process_handler_event (NCDModuleProcess *process, int event); +static void deinit_process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void deinit_timer_handler (struct instance *o); +static void instance_free (struct instance *o); + +static int start_process (struct instance *o, NCDValRef template_name, NCDValRef args, NCDModuleProcess_handler_event handler) +{ + ASSERT(NCDVal_IsString(template_name)) + ASSERT(NCDVal_IsList(args)) + + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name, args, handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + return 0; + } + + // set special functions + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + return 1; +} + +static void go_deinit (struct instance *o) +{ + ASSERT(o->dying) + + // deinit is no-op? + if (ncd_is_none(o->deinit_template)) { + instance_free(o); + return; + } + + // start deinit process + if (!start_process(o, o->deinit_template, o->deinit_args, deinit_process_handler_event)) { + instance_free(o); + return; + } + + // start timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->deinit_timer); + + // set state deinit working + o->state = STATE_DEINIT_WORKING; +} + +static void init_process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_INIT_WORKING) + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state init cleaning + o->state = STATE_INIT_CLEANING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_INIT_CLEANING) + + // free process + NCDModuleProcess_Free(&o->process); + + // were we requested to die aleady? + if (o->dying) { + go_deinit(o); + return; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state up + o->state = STATE_UP; + } break; + + default: ASSERT(0); + } +} + +static void deinit_process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->dying) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_DEINIT_WORKING) + + // stop timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->deinit_timer); + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state deinit cleaning + o->state = STATE_DEINIT_CLEANING; + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_DEINIT_CLEANING) + + // free process + NCDModuleProcess_Free(&o->process); + + // die + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->state != STATE_UP) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj); + return 1; + } + + return 0; +} + +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + ASSERT(o->state != STATE_UP) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void deinit_timer_handler (struct instance *o) +{ + ASSERT(o->state == STATE_DEINIT_WORKING) + + ModuleLog(o->i, BLOG_ERROR, "imperative deinit timeout elapsed"); + + // start terminating + NCDModuleProcess_Terminate(&o->process); + + // set state deinit cleaning + o->state = STATE_DEINIT_CLEANING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef init_template_arg; + NCDValRef init_args; + NCDValRef deinit_template_arg; + NCDValRef deinit_timeout_arg; + if (!NCDVal_ListRead(params->args, 5, &init_template_arg, &init_args, &deinit_template_arg, &o->deinit_args, &deinit_timeout_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(init_template_arg) || !NCDVal_IsList(init_args) || + !NCDVal_IsString(deinit_template_arg) || !NCDVal_IsList(o->deinit_args) || + !NCDVal_IsString(deinit_timeout_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->deinit_template = deinit_template_arg; + + // read timeout + uintmax_t timeout; + if (!ncd_read_uintmax(deinit_timeout_arg, &timeout) || timeout > UINT64_MAX){ + ModuleLog(i, BLOG_ERROR, "wrong timeout"); + goto fail0; + } + + // init timer + BTimer_Init(&o->deinit_timer, timeout, (BTimer_handler)deinit_timer_handler, o); + + if (ncd_is_none(init_template_arg)) { + // signal up + NCDModuleInst_Backend_Up(i); + + // set state up + o->state = STATE_UP; + } else { + // start init process + if (!start_process(o, init_template_arg, init_args, init_process_handler_event)) { + goto fail0; + } + + // set state init working + o->state = STATE_INIT_WORKING; + } + + // set not dying + o->dying = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // set dying + o->dying = 1; + + if (o->state == STATE_UP) { + go_deinit(o); + return; + } +} + +static struct NCDModule modules[] = { + { + .type = "imperative", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_imperative = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/implode.c b/external/badvpn_dns/ncd/modules/implode.c new file mode 100644 index 00000000..0520ecab --- /dev/null +++ b/external/badvpn_dns/ncd/modules/implode.c @@ -0,0 +1,155 @@ +/** + * @file implode.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * implode(string glue, list(string) pieces) + * + * Variables: + * string (empty) - concatenation of strings in 'pieces', with 'glue' in between + * every two elements. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + char *result; + size_t result_len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef glue_arg; + NCDValRef pieces_arg; + if (!NCDVal_ListRead(params->args, 2, &glue_arg, &pieces_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(glue_arg) || !NCDVal_IsList(pieces_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init result string + ExpString str; + if (!ExpString_Init(&str)) { + ModuleLog(i, BLOG_ERROR, "ExpString_Init failed"); + goto fail0; + } + + size_t count = NCDVal_ListCount(pieces_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef piece = NCDVal_ListGet(pieces_arg, j); + + // check piece type + if (!NCDVal_IsString(piece)) { + ModuleLog(i, BLOG_ERROR, "wrong piece type"); + goto fail1; + } + + // append glue + if (j > 0) { + if (!ExpString_AppendBinary(&str, (const uint8_t *)NCDVal_StringData(glue_arg), NCDVal_StringLength(glue_arg))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + } + + // append piece + if (!ExpString_AppendBinary(&str, (const uint8_t *)NCDVal_StringData(piece), NCDVal_StringLength(piece))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail1; + } + } + + // store result + o->result = ExpString_Get(&str); + o->result_len = ExpString_Length(&str); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + ExpString_Free(&str); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free result + free(o->result); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->result, o->result_len); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "implode", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_implode = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/index.c b/external/badvpn_dns/ncd/modules/index.c new file mode 100644 index 00000000..fe68bddd --- /dev/null +++ b/external/badvpn_dns/ncd/modules/index.c @@ -0,0 +1,164 @@ +/** + * @file index.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * index index(string value) + * index index::next() + * + * Description: + * Non-negative integer with range of a size_t. + * The first form creates an index from the given decimal string. + * The second form cretes an index with value one more than an existing + * index. + * + * Variables: + * string (empty) - the index value. Note this may be different from + * than the value given to index() if it was not in normal form. + */ + +#include +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + size_t value; +}; + +static void func_new_templ (void *vo, NCDModuleInst *i, size_t value) +{ + struct instance *o = vo; + o->i = i; + + // set value + o->value = value; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void func_new_from_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef arg_value; + if (!NCDVal_ListRead(params->args, 1, &arg_value)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_value)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse value + uintmax_t value; + if (!ncd_read_uintmax(arg_value, &value)) { + ModuleLog(i, BLOG_ERROR, "wrong value"); + goto fail0; + } + + // check overflow + if (value > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "value too large"); + goto fail0; + } + + func_new_templ(vo, i, value); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_from_index (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *index = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check overflow + if (index->value == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "overflow"); + goto fail0; + } + + func_new_templ(vo, i, index->value + 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_uintmax(mem, o->value); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "index", + .func_new2 = func_new_from_value, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "index::next", + .base_type = "index", + .func_new2 = func_new_from_index, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_index = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/list.c b/external/badvpn_dns/ncd/modules/list.c new file mode 100644 index 00000000..e1df1f11 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/list.c @@ -0,0 +1,871 @@ +/** + * @file list.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * List construction module. + * + * Synopsis: + * list(elem1, ..., elemN) + * list listfrom(list l1, ..., list lN) + * + * Description: + * The first form creates a list with the given elements. + * The second form creates a list by concatenating the given + * lists. + * + * Variables: + * (empty) - list containing elem1, ..., elemN + * length - number of elements in list + * + * Synopsis: list::append(arg) + * + * Synopsis: list::appendv(list arg) + * Description: Appends the elements of arg to the list. + * + * Synopsis: list::length() + * Variables: + * (empty) - number of elements in list at the time of initialization + * of this method + * + * Synopsis: list::get(string index) + * Variables: + * (empty) - element of list at position index (starting from zero) at the time of initialization + * + * Synopsis: list::shift() + * + * Synopsis: list::contains(value) + * Variables: + * (empty) - "true" if list contains value, "false" if not + * + * Synopsis: + * list::find(start_pos, value) + * Description: + * finds the first occurrence of 'value' in the list at position >='start_pos'. + * Variables: + * pos - position of element, or "none" if not found + * found - "true" if found, "false" if not + * + * Sysnopsis: + * list::remove_at(remove_pos) + * Description: + * Removes the element at position 'remove_pos', which must refer to an existing element. + * + * Synopsis: + * list::remove(value) + * Description: + * Removes the first occurrence of value in the list, which must be in the list. + * + * Synopsis: + * list::set(list l1, ..., list lN) + * Description: + * Replaces the list with the concatenation of given lists. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct elem { + IndexedListNode il_node; + NCDValMem mem; + NCDValRef val; +}; + +struct instance { + NCDModuleInst *i; + IndexedList il; +}; + +struct length_instance { + NCDModuleInst *i; + uint64_t length; +}; + +struct get_instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef val; +}; + +struct contains_instance { + NCDModuleInst *i; + int contains; +}; + +struct find_instance { + NCDModuleInst *i; + int is_found; + uint64_t found_pos; +}; + +static uint64_t list_count (struct instance *o) +{ + return IndexedList_Count(&o->il); +} + +static struct elem * insert_value (NCDModuleInst *i, struct instance *o, NCDValRef val, uint64_t idx) +{ + ASSERT(idx <= list_count(o)) + ASSERT(!NCDVal_IsInvalid(val)) + + struct elem *e = malloc(sizeof(*e)); + if (!e) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + NCDValMem_Init(&e->mem); + + e->val = NCDVal_NewCopy(&e->mem, val); + if (NCDVal_IsInvalid(e->val)) { + goto fail1; + } + + IndexedList_InsertAt(&o->il, &e->il_node, idx); + + return e; + +fail1: + NCDValMem_Free(&e->mem); + free(e); +fail0: + return NULL; +} + +static void remove_elem (struct instance *o, struct elem *e) +{ + IndexedList_Remove(&o->il, &e->il_node); + NCDValMem_Free(&e->mem); + free(e); +} + +static struct elem * get_elem_at (struct instance *o, uint64_t idx) +{ + ASSERT(idx < list_count(o)) + + IndexedListNode *iln = IndexedList_GetAt(&o->il, idx); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static struct elem * get_first_elem (struct instance *o) +{ + ASSERT(list_count(o) > 0) + + IndexedListNode *iln = IndexedList_GetFirst(&o->il); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static struct elem * get_last_elem (struct instance *o) +{ + ASSERT(list_count(o) > 0) + + IndexedListNode *iln = IndexedList_GetLast(&o->il); + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + return e; +} + +static void cut_list_front (struct instance *o, uint64_t count) +{ + while (list_count(o) > count) { + remove_elem(o, get_first_elem(o)); + } +} + +static void cut_list_back (struct instance *o, uint64_t count) +{ + while (list_count(o) > count) { + remove_elem(o, get_last_elem(o)); + } +} + +static int append_list_contents (NCDModuleInst *i, struct instance *o, NCDValRef args) +{ + ASSERT(NCDVal_IsList(args)) + + uint64_t orig_count = list_count(o); + + size_t append_count = NCDVal_ListCount(args); + + for (size_t j = 0; j < append_count; j++) { + NCDValRef elem = NCDVal_ListGet(args, j); + if (!insert_value(i, o, elem, list_count(o))) { + goto fail; + } + } + + return 1; + +fail: + cut_list_back(o, orig_count); + return 0; +} + +static int append_list_contents_contents (NCDModuleInst *i, struct instance *o, NCDValRef args) +{ + ASSERT(NCDVal_IsList(args)) + + uint64_t orig_count = list_count(o); + + size_t append_count = NCDVal_ListCount(args); + + for (size_t j = 0; j < append_count; j++) { + NCDValRef elem = NCDVal_ListGet(args, j); + + if (!NCDVal_IsList(elem)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail; + } + + if (!append_list_contents(i, o, elem)) { + goto fail; + } + } + + return 1; + +fail: + cut_list_back(o, orig_count); + return 0; +} + +static struct elem * find_elem (struct instance *o, NCDValRef val, uint64_t start_idx, uint64_t *out_idx) +{ + if (start_idx >= list_count(o)) { + return NULL; + } + + for (IndexedListNode *iln = IndexedList_GetAt(&o->il, start_idx); iln; iln = IndexedList_GetNext(&o->il, iln)) { + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + if (NCDVal_Compare(e->val, val) == 0) { + if (out_idx) { + *out_idx = start_idx; + } + return e; + } + start_idx++; + } + + return NULL; +} + +static int list_to_value (NCDModuleInst *i, struct instance *o, NCDValMem *mem, NCDValRef *out_val) +{ + *out_val = NCDVal_NewList(mem, IndexedList_Count(&o->il)); + if (NCDVal_IsInvalid(*out_val)) { + goto fail; + } + + for (IndexedListNode *iln = IndexedList_GetFirst(&o->il); iln; iln = IndexedList_GetNext(&o->il, iln)) { + struct elem *e = UPPER_OBJECT(iln, struct elem, il_node); + + NCDValRef copy = NCDVal_NewCopy(mem, e->val); + if (NCDVal_IsInvalid(copy)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out_val, copy)) { + goto fail; + } + } + + return 1; + +fail: + return 0; +} + +static void func_new_list (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init list + IndexedList_Init(&o->il); + + // append contents + if (!append_list_contents(i, o, params->args)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + cut_list_front(o, 0); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_listfrom (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init list + IndexedList_Init(&o->il); + + // append contents contents + if (!append_list_contents_contents(i, o, params->args)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + cut_list_front(o, 0); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free list elements + cut_list_front(o, 0); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + if (!list_to_value(o->i, o, mem, out)) { + return 0; + } + + return 1; + } + + if (!strcmp(name, "length")) { + *out = ncd_make_uintmax(mem, list_count(o)); + return 1; + } + + return 0; +} + +static void append_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // append + if (!insert_value(i, mo, arg, list_count(mo))) { + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void appendv_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // append + if (!append_list_contents(i, mo, arg)) { + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void length_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct length_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // remember length + o->length = list_count(mo); + + // signal up + NCDModuleInst_Backend_Up(o->i); + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void length_func_die (void *vo) +{ + struct length_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int length_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct length_instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_uintmax(mem, o->length); + return 1; + } + + return 0; +} + +static void get_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct get_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef index_arg; + if (!NCDVal_ListRead(params->args, 1, &index_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(index_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + uintmax_t index; + if (!ncd_read_uintmax(index_arg, &index)) { + ModuleLog(o->i, BLOG_ERROR, "wrong value"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check index + if (index >= list_count(mo)) { + ModuleLog(o->i, BLOG_ERROR, "no element at index %"PRIuMAX, index); + goto fail0; + } + + // get element + struct elem *e = get_elem_at(mo, index); + + // init mem + NCDValMem_Init(&o->mem); + + // copy value + o->val = NCDVal_NewCopy(&o->mem, e->val); + if (NCDVal_IsInvalid(o->val)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void get_func_die (void *vo) +{ + struct get_instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int get_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct get_instance *o = vo; + + if (!strcmp(name, "")) { + *out = NCDVal_NewCopy(mem, o->val); + return 1; + } + + return 0; +} + +static void shift_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check first + if (list_count(mo) == 0) { + ModuleLog(i, BLOG_ERROR, "list has no elements"); + goto fail0; + } + + // remove first + remove_elem(mo, get_first_elem(mo)); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void contains_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct contains_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // search + o->contains = !!find_elem(mo, value_arg, 0, NULL); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void contains_func_die (void *vo) +{ + struct contains_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int contains_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct contains_instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->contains, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void find_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct find_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef start_pos_arg; + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 2, &start_pos_arg, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(start_pos_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read start position + uintmax_t start_pos; + if (!ncd_read_uintmax(start_pos_arg, &start_pos) || start_pos > UINT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong start pos"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // find + o->is_found = !!find_elem(mo, value_arg, start_pos, &o->found_pos); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void find_func_die (void *vo) +{ + struct find_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int find_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct find_instance *o = vo; + + if (!strcmp(name, "pos")) { + char value[64] = "none"; + + if (o->is_found) { + generate_decimal_repr_string(o->found_pos, value); + } + + *out = NCDVal_NewString(mem, value); + return 1; + } + + if (!strcmp(name, "found")) { + *out = ncd_make_boolean(mem, o->is_found, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void removeat_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef remove_pos_arg; + if (!NCDVal_ListRead(params->args, 1, &remove_pos_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(remove_pos_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read position + uintmax_t remove_pos; + if (!ncd_read_uintmax(remove_pos_arg, &remove_pos)) { + ModuleLog(i, BLOG_ERROR, "wrong pos"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // check position + if (remove_pos >= list_count(mo)) { + ModuleLog(i, BLOG_ERROR, "pos out of range"); + goto fail0; + } + + // remove + remove_elem(mo, get_elem_at(mo, remove_pos)); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void remove_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // find element + struct elem *e = find_elem(mo, value_arg, 0, NULL); + if (!e) { + ModuleLog(i, BLOG_ERROR, "value does not exist"); + goto fail0; + } + + // remove element + remove_elem(mo, e); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void set_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // remember old count + uint64_t old_count = list_count(mo); + + // append contents of our lists + if (!append_list_contents_contents(i, mo, params->args)) { + goto fail0; + } + + // remove old elements + cut_list_front(mo, list_count(mo) - old_count); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "list", + .func_new2 = func_new_list, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "listfrom", + .base_type = "list", + .func_new2 = func_new_listfrom, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "concatlist", // alias for listfrom + .base_type = "list", + .func_new2 = func_new_listfrom, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "list::append", + .func_new2 = append_func_new + }, { + .type = "list::appendv", + .func_new2 = appendv_func_new + }, { + .type = "list::length", + .func_new2 = length_func_new, + .func_die = length_func_die, + .func_getvar = length_func_getvar, + .alloc_size = sizeof(struct length_instance) + }, { + .type = "list::get", + .func_new2 = get_func_new, + .func_die = get_func_die, + .func_getvar = get_func_getvar, + .alloc_size = sizeof(struct get_instance) + }, { + .type = "list::shift", + .func_new2 = shift_func_new + }, { + .type = "list::contains", + .func_new2 = contains_func_new, + .func_die = contains_func_die, + .func_getvar = contains_func_getvar, + .alloc_size = sizeof(struct contains_instance) + }, { + .type = "list::find", + .func_new2 = find_func_new, + .func_die = find_func_die, + .func_getvar = find_func_getvar, + .alloc_size = sizeof(struct find_instance) + }, { + .type = "list::remove_at", + .func_new2 = removeat_func_new + }, { + .type = "list::remove", + .func_new2 = remove_func_new + }, { + .type = "list::set", + .func_new2 = set_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_list = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/load_module.c b/external/badvpn_dns/ncd/modules/load_module.c new file mode 100644 index 00000000..f9bf8327 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/load_module.c @@ -0,0 +1,313 @@ +/** + * @file load_module.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * load_module(string name) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct global { + LinkedList0 modules_list; +}; + +struct module { + char *name; + void *lib_handle; + int ncdmodule_loaded; + LinkedList0Node modules_list_node; +}; + +static struct module * find_module (const char *name, struct global *g) +{ + for (LinkedList0Node *ln = LinkedList0_GetFirst(&g->modules_list); ln; ln = LinkedList0Node_Next(ln)) { + struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node); + if (!strcmp(mod->name, name)) { + return mod; + } + } + return NULL; +} + +static struct module * module_init (const char *name, NCDModuleInst *i) +{ + struct global *g = ModuleGlobal(i); + ASSERT(!find_module(name, g)) + + struct module *mod = BAlloc(sizeof(*mod)); + if (!mod) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + mod->name = b_strdup(name); + if (!mod->name) { + ModuleLog(i, BLOG_ERROR, "b_strdup failed"); + goto fail1; + } + + mod->lib_handle = NULL; + mod->ncdmodule_loaded = 0; + LinkedList0_Prepend(&g->modules_list, &mod->modules_list_node); + + return mod; + +fail1: + BFree(mod); +fail0: + return NULL; +} + +static void module_free (struct module *mod, struct global *g) +{ + LinkedList0_Remove(&g->modules_list, &mod->modules_list_node); + if (mod->lib_handle) { + if (dlclose(mod->lib_handle) != 0) { + BLog(BLOG_ERROR, "dlclose failed"); + } + } + BFree(mod->name); + BFree(mod); +} + +static char * x_read_link (const char *path) +{ + size_t size = 32; + char *buf = BAlloc(size + 1); + if (!buf) { + goto fail0; + } + + ssize_t link_size; + while (1) { + link_size = readlink(path, buf, size); + if (link_size < 0) { + goto fail1; + } + if (link_size >= 0 && link_size < size) { + break; + } + if (size > SIZE_MAX / 2 || 2 * size > SIZE_MAX - 1) { + goto fail1; + } + size *= 2; + char *new_buf = BRealloc(buf, size + 1); + if (!new_buf) { + goto fail1; + } + buf = new_buf; + } + + buf[link_size] = '\0'; + return buf; + +fail1: + BFree(buf); +fail0: + return NULL; +} + +static char * find_module_library (NCDModuleInst *i, const char *module_name) +{ + char *ret = NULL; + + char *self = x_read_link("/proc/self/exe"); + if (!self) { + ModuleLog(i, BLOG_ERROR, "failed to read /proc/self/exe"); + goto fail0; + } + + char *slash = strrchr(self, '/'); + if (!slash) { + ModuleLog(i, BLOG_ERROR, "contents of /proc/self/exe do not have a slash"); + goto fail1; + } + *slash = '\0'; + + const char *paths[] = {"../lib/badvpn-ncd", "../mcvpn", NULL}; + + size_t j; + for (j = 0; paths[j]; j++) { + char *module_path = concat_strings(6, self, "/", paths[j], "/libncdmodule_", module_name, ".so"); + if (!module_path) { + ModuleLog(i, BLOG_ERROR, "concat_strings failed"); + goto fail1; + } + + if (access(module_path, F_OK) == 0) { + ret = module_path; + break; + } + + BFree(module_path); + } + + if (!paths[j]) { + ModuleLog(i, BLOG_ERROR, "failed to find module"); + } + +fail1: + BFree(self); +fail0: + return ret; +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + group->group_state = g; + LinkedList0_Init(&g->modules_list); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + + LinkedList0Node *ln; + while ((ln = LinkedList0_GetFirst(&g->modules_list))) { + struct module *mod = UPPER_OBJECT(ln, struct module, modules_list_node); + module_free(mod, g); + } + + BFree(g); +} + +static void func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct module *mod = find_module(NCDVal_StringData(name_arg), ModuleGlobal(i)); + ASSERT(!mod || mod->lib_handle) + + if (!mod) { + mod = module_init(NCDVal_StringData(name_arg), i); + if (!mod) { + ModuleLog(i, BLOG_ERROR, "module_init failed"); + goto fail0; + } + + // find module library + char *module_path = find_module_library(i, NCDVal_StringData(name_arg)); + if (!module_path) { + module_free(mod, ModuleGlobal(i)); + goto fail0; + } + + // load it as a dynamic library + mod->lib_handle = dlopen(module_path, RTLD_NOW); + BFree(module_path); + if (!mod->lib_handle) { + ModuleLog(i, BLOG_ERROR, "dlopen failed"); + module_free(mod, ModuleGlobal(i)); + goto fail0; + } + } + + if (!mod->ncdmodule_loaded) { + // build name of NCDModuleGroup structure symbol + char *group_symbol = concat_strings(2, "ncdmodule_", NCDVal_StringData(name_arg)); + if (!group_symbol) { + ModuleLog(i, BLOG_ERROR, "concat_strings failed"); + goto fail0; + } + + // resolve NCDModuleGroup structure symbol + void *group = dlsym(mod->lib_handle, group_symbol); + BFree(group_symbol); + if (!group) { + ModuleLog(i, BLOG_ERROR, "dlsym failed"); + goto fail0; + } + + // load module group + if (!NCDModuleInst_Backend_InterpLoadGroup(i, (struct NCDModuleGroup *)group)) { + ModuleLog(i, BLOG_ERROR, "NCDModuleInst_Backend_InterpLoadGroup failed"); + goto fail0; + } + + mod->ncdmodule_loaded = 1; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "load_module", + .func_new2 = func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_load_module = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/log.c b/external/badvpn_dns/ncd/modules/log.c new file mode 100644 index 00000000..5b4251de --- /dev/null +++ b/external/badvpn_dns/ncd/modules/log.c @@ -0,0 +1,285 @@ +/** + * @file log.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Message logging using the BLog system provided by the BadVPN framework. + * Each message has an associated loglevel, which must be one of: "error, "warning", + * "notice", "info", "debug", or a numeric identifier (1=error to 5=debug). + * + * Synopsis: + * log(string level [, string ...]) + * + * Description: + * On init, logs the concatenation of the given strings. + * + * Synopsis: + * log_r(string level [, string ...]) + * + * Description: + * On deinit, logs the concatenation of the given strings. + * + * Synopsis: + * log_fr(string level, list(string) strings_init, list(string) strings_deinit) + * + * Description: + * On init, logs the concatenation of the strings in 'strings_init', + * and on deinit, logs the concatenation of the strings in 'strings_deinit'. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct rlog_instance { + NCDModuleInst *i; + int level; + NCDValRef list; + size_t start; +}; + +enum {STRING_ERROR, STRING_WARNING, STRING_NOTICE, STRING_INFO, STRING_DEBUG}; + +static const char *strings[] = { + "error", "warning", "notice", "info", "debug", NULL +}; + +static int check_strings (NCDValRef list, size_t start) +{ + ASSERT(NCDVal_IsList(list)) + + size_t count = NCDVal_ListCount(list); + + for (size_t j = start; j < count; j++) { + NCDValRef string = NCDVal_ListGet(list, j); + if (!NCDVal_IsString(string)) { + return 0; + } + } + + return 1; +} + +static void do_log (int level, NCDValRef list, size_t start) +{ + ASSERT(level >= BLOG_ERROR) + ASSERT(level <= BLOG_DEBUG) + ASSERT(check_strings(list, start)) + + if (!BLog_WouldLog(BLOG_CHANNEL_ncd_log_msg, level)) { + return; + } + + size_t count = NCDVal_ListCount(list); + + BLog_Begin(); + + for (size_t j = start; j < count; j++) { + NCDValRef string = NCDVal_ListGet(list, j); + ASSERT(NCDVal_IsString(string)) + BLog_AppendBytes(NCDVal_StringData(string), NCDVal_StringLength(string)); + } + + BLog_Finish(BLOG_CHANNEL_ncd_log_msg, level); +} + +static int parse_level (NCDModuleInst *i, NCDValRef level_arg, int *out_level) +{ + if (!NCDVal_IsString(level_arg)) { + return 0; + } + + NCDStringIndex *string_index = i->params->iparams->string_index; + + uintmax_t level_numeric; + if (ncd_read_uintmax(level_arg, &level_numeric) && level_numeric >= BLOG_ERROR && level_numeric <= BLOG_DEBUG) { + *out_level = level_numeric; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_ERROR), string_index)) { + *out_level = BLOG_ERROR; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_WARNING), string_index)) { + *out_level = BLOG_WARNING; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_NOTICE), string_index)) { + *out_level = BLOG_NOTICE; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_INFO), string_index)) { + *out_level = BLOG_INFO; + } + else if (NCDVal_StringEqualsId(level_arg, ModuleString(i, STRING_DEBUG), string_index)) { + *out_level = BLOG_DEBUG; + } + else { + return 0; + } + + return 1; +} + +static void rlog_func_new_common (void *vo, NCDModuleInst *i, int level, NCDValRef list, size_t start) +{ + ASSERT(level >= BLOG_ERROR) + ASSERT(level <= BLOG_DEBUG) + ASSERT(check_strings(list, start)) + + struct rlog_instance *o = vo; + o->i = i; + o->level = level; + o->list = list; + o->start = start; + + NCDModuleInst_Backend_Up(i); +} + +static void rlog_func_die (void *vo) +{ + struct rlog_instance *o = vo; + + do_log(o->level, o->list, o->start); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void log_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (NCDVal_ListCount(params->args) < 1) { + ModuleLog(i, BLOG_ERROR, "missing level argument"); + goto fail0; + } + + int level; + if (!parse_level(i, NCDVal_ListGet(params->args, 0), &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!check_strings(params->args, 1)) { + ModuleLog(i, BLOG_ERROR, "wrong string arguments"); + goto fail0; + } + + do_log(level, params->args, 1); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void log_r_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (NCDVal_ListCount(params->args) < 1) { + ModuleLog(i, BLOG_ERROR, "missing level argument"); + goto fail0; + } + + int level; + if (!parse_level(i, NCDVal_ListGet(params->args, 0), &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!check_strings(params->args, 1)) { + ModuleLog(i, BLOG_ERROR, "wrong string arguments"); + goto fail0; + } + + rlog_func_new_common(vo, i, level, params->args, 1); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void log_fr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef level_arg; + NCDValRef strings_init_arg; + NCDValRef strings_deinit_arg; + if (!NCDVal_ListRead(params->args, 3, &level_arg, &strings_init_arg, &strings_deinit_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + int level; + if (!parse_level(i, level_arg, &level)) { + ModuleLog(i, BLOG_ERROR, "wrong level argument"); + goto fail0; + } + + if (!NCDVal_IsList(strings_init_arg) || !check_strings(strings_init_arg, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong string_init argument"); + goto fail0; + } + + if (!NCDVal_IsList(strings_deinit_arg) || !check_strings(strings_deinit_arg, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong strings_deinit argument"); + goto fail0; + } + + do_log(level, strings_init_arg, 0); + + rlog_func_new_common(vo, i, level, strings_deinit_arg, 0); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "log", + .func_new2 = log_func_new + }, { + .type = "log_r", + .func_new2 = log_r_func_new, + .func_die = rlog_func_die, + .alloc_size = sizeof(struct rlog_instance) + }, { + .type = "log_fr", + .func_new2 = log_fr_func_new, + .func_die = rlog_func_die, + .alloc_size = sizeof(struct rlog_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_log = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/logical.c b/external/badvpn_dns/ncd/modules/logical.c new file mode 100644 index 00000000..8ac6660e --- /dev/null +++ b/external/badvpn_dns/ncd/modules/logical.c @@ -0,0 +1,160 @@ +/** + * @file logical.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for logical operators. + * + * Synopsis: not(string val) + * Variables: + * string (empty) - "true" if val does not equal "true", "false" otherwise + * + * Synopsis: or([string val1, ...]) + * Variables: + * string (empty) - "true" if at least one of the values equals "true", "false" otherwise + * + * Synopsis: and([string val1, ...]) + * Variables: + * string (empty) - "true" if all of the values equal "true", "false" otherwise + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_not, int is_or) +{ + struct instance *o = vo; + o->i = i; + + // compute value from arguments + if (is_not) { + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->value = !ncd_read_boolean(arg); + } else { + o->value = (is_or ? 0 : 1); + + size_t count = NCDVal_ListCount(params->args); + + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(params->args, j); + + if (!NCDVal_IsString(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + int this_value = ncd_read_boolean(arg); + if (is_or) { + o->value = o->value || this_value; + } else { + o->value = o->value && this_value; + } + } + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_not (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 1, 0); +} + +static void func_new_or (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 0, 1); +} + +static void func_new_and (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, 0, 0); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "not", + .func_new2 = func_new_not, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "or", + .func_new2 = func_new_or, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "and", + .func_new2 = func_new_and, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_logical = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/modules.h b/external/badvpn_dns/ncd/modules/modules.h new file mode 100644 index 00000000..ea390274 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/modules.h @@ -0,0 +1,210 @@ +/** + * @file modules.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_NCD_MODULES_MODULES_H +#define BADVPN_NCD_MODULES_MODULES_H + +#include + +#include + +extern const struct NCDModuleGroup ncdmodule_var; +extern const struct NCDModuleGroup ncdmodule_list; +extern const struct NCDModuleGroup ncdmodule_depend; +extern const struct NCDModuleGroup ncdmodule_multidepend; +extern const struct NCDModuleGroup ncdmodule_dynamic_depend; +extern const struct NCDModuleGroup ncdmodule_concat; +extern const struct NCDModuleGroup ncdmodule_if; +extern const struct NCDModuleGroup ncdmodule_strcmp; +extern const struct NCDModuleGroup ncdmodule_logical; +extern const struct NCDModuleGroup ncdmodule_sleep; +extern const struct NCDModuleGroup ncdmodule_print; +extern const struct NCDModuleGroup ncdmodule_blocker; +extern const struct NCDModuleGroup ncdmodule_spawn; +extern const struct NCDModuleGroup ncdmodule_imperative; +extern const struct NCDModuleGroup ncdmodule_ref; +extern const struct NCDModuleGroup ncdmodule_index; +extern const struct NCDModuleGroup ncdmodule_alias; +extern const struct NCDModuleGroup ncdmodule_process_manager; +extern const struct NCDModuleGroup ncdmodule_ondemand; +extern const struct NCDModuleGroup ncdmodule_foreach; +extern const struct NCDModuleGroup ncdmodule_choose; +extern const struct NCDModuleGroup ncdmodule_from_string; +extern const struct NCDModuleGroup ncdmodule_to_string; +extern const struct NCDModuleGroup ncdmodule_value; +extern const struct NCDModuleGroup ncdmodule_try; +extern const struct NCDModuleGroup ncdmodule_exit; +extern const struct NCDModuleGroup ncdmodule_getargs; +extern const struct NCDModuleGroup ncdmodule_arithmetic; +extern const struct NCDModuleGroup ncdmodule_parse; +extern const struct NCDModuleGroup ncdmodule_valuemetic; +extern const struct NCDModuleGroup ncdmodule_file; +extern const struct NCDModuleGroup ncdmodule_netmask; +extern const struct NCDModuleGroup ncdmodule_implode; +extern const struct NCDModuleGroup ncdmodule_call2; +extern const struct NCDModuleGroup ncdmodule_assert; +extern const struct NCDModuleGroup ncdmodule_explode; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network; +extern const struct NCDModuleGroup ncdmodule_timer; +extern const struct NCDModuleGroup ncdmodule_file_open; +extern const struct NCDModuleGroup ncdmodule_backtrack; +extern const struct NCDModuleGroup ncdmodule_depend_scope; +extern const struct NCDModuleGroup ncdmodule_substr; +extern const struct NCDModuleGroup ncdmodule_log; +extern const struct NCDModuleGroup ncdmodule_buffer; +extern const struct NCDModuleGroup ncdmodule_getenv; +#ifndef BADVPN_EMSCRIPTEN +extern const struct NCDModuleGroup ncdmodule_regex_match; +extern const struct NCDModuleGroup ncdmodule_run; +extern const struct NCDModuleGroup ncdmodule_runonce; +extern const struct NCDModuleGroup ncdmodule_daemon; +extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice; +extern const struct NCDModuleGroup ncdmodule_net_backend_waitlink; +extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn; +extern const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant; +#ifdef BADVPN_USE_LINUX_RFKILL +extern const struct NCDModuleGroup ncdmodule_net_backend_rfkill; +#endif +extern const struct NCDModuleGroup ncdmodule_net_up; +extern const struct NCDModuleGroup ncdmodule_net_dns; +extern const struct NCDModuleGroup ncdmodule_net_iptables; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_route; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp; +extern const struct NCDModuleGroup ncdmodule_net_ipv4_arp_probe; +extern const struct NCDModuleGroup ncdmodule_net_watch_interfaces; +extern const struct NCDModuleGroup ncdmodule_sys_watch_input; +extern const struct NCDModuleGroup ncdmodule_sys_watch_usb; +#ifdef BADVPN_USE_LINUX_INPUT +extern const struct NCDModuleGroup ncdmodule_sys_evdev; +#endif +#ifdef BADVPN_USE_INOTIFY +extern const struct NCDModuleGroup ncdmodule_sys_watch_directory; +#endif +extern const struct NCDModuleGroup ncdmodule_sys_request_server; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_wait_dynamic_addr; +extern const struct NCDModuleGroup ncdmodule_sys_request_client; +extern const struct NCDModuleGroup ncdmodule_reboot; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_addr; +extern const struct NCDModuleGroup ncdmodule_net_ipv6_route; +extern const struct NCDModuleGroup ncdmodule_socket; +extern const struct NCDModuleGroup ncdmodule_sys_start_process; +extern const struct NCDModuleGroup ncdmodule_load_module; +#endif + +static const struct NCDModuleGroup *ncd_modules[] = { + &ncdmodule_var, + &ncdmodule_list, + &ncdmodule_depend, + &ncdmodule_multidepend, + &ncdmodule_dynamic_depend, + &ncdmodule_concat, + &ncdmodule_if, + &ncdmodule_strcmp, + &ncdmodule_logical, + &ncdmodule_sleep, + &ncdmodule_print, + &ncdmodule_blocker, + &ncdmodule_spawn, + &ncdmodule_imperative, + &ncdmodule_ref, + &ncdmodule_index, + &ncdmodule_alias, + &ncdmodule_process_manager, + &ncdmodule_ondemand, + &ncdmodule_foreach, + &ncdmodule_choose, + &ncdmodule_from_string, + &ncdmodule_to_string, + &ncdmodule_value, + &ncdmodule_try, + &ncdmodule_exit, + &ncdmodule_getargs, + &ncdmodule_arithmetic, + &ncdmodule_parse, + &ncdmodule_valuemetic, + &ncdmodule_file, + &ncdmodule_netmask, + &ncdmodule_implode, + &ncdmodule_call2, + &ncdmodule_assert, + &ncdmodule_explode, + &ncdmodule_net_ipv4_addr_in_network, + &ncdmodule_net_ipv6_addr_in_network, + &ncdmodule_timer, + &ncdmodule_file_open, + &ncdmodule_backtrack, + &ncdmodule_depend_scope, + &ncdmodule_substr, + &ncdmodule_log, + &ncdmodule_buffer, + &ncdmodule_getenv, +#ifndef BADVPN_EMSCRIPTEN + &ncdmodule_regex_match, + &ncdmodule_run, + &ncdmodule_runonce, + &ncdmodule_daemon, + &ncdmodule_net_backend_waitdevice, + &ncdmodule_net_backend_waitlink, + &ncdmodule_net_backend_badvpn, + &ncdmodule_net_backend_wpa_supplicant, +#ifdef BADVPN_USE_LINUX_RFKILL + &ncdmodule_net_backend_rfkill, +#endif + &ncdmodule_net_up, + &ncdmodule_net_dns, + &ncdmodule_net_iptables, + &ncdmodule_net_ipv4_addr, + &ncdmodule_net_ipv4_route, + &ncdmodule_net_ipv4_dhcp, + &ncdmodule_net_ipv4_arp_probe, + &ncdmodule_net_watch_interfaces, + &ncdmodule_sys_watch_input, + &ncdmodule_sys_watch_usb, +#ifdef BADVPN_USE_LINUX_INPUT + &ncdmodule_sys_evdev, +#endif +#ifdef BADVPN_USE_INOTIFY + &ncdmodule_sys_watch_directory, +#endif + &ncdmodule_sys_request_server, + &ncdmodule_net_ipv6_wait_dynamic_addr, + &ncdmodule_sys_request_client, + &ncdmodule_reboot, + &ncdmodule_net_ipv6_addr, + &ncdmodule_net_ipv6_route, + &ncdmodule_socket, + &ncdmodule_sys_start_process, + &ncdmodule_load_module, +#endif + NULL +}; + +#endif diff --git a/external/badvpn_dns/ncd/modules/multidepend.c b/external/badvpn_dns/ncd/modules/multidepend.c new file mode 100644 index 00000000..9b201ae7 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/multidepend.c @@ -0,0 +1,401 @@ +/** + * @file multidepend.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * This is a compatibility module. It behaves exactly like the depend_scope module, + * except that there is a single global scope for dependency names. + * + * Use depend_scope instead. If you are using multidepend between non-template + * processes, make those processes templates instead and start them via + * process_manager(). For example, instead of this: + * + * process foo { + * multiprovide("FOO"); + * } + * + * process bar { + * multidepend({"FOO"}); + * } + * + * Use this: + * + * process main { + * depend_scope() scope; + * process_manager() mgr; + * mgr->start("foo", "foo", {}); + * mgr->start("bar", "bar", {}); + * } + * + * template foo { + * _caller.scope->provide("FOO"); + * } + * + * template bar { + * _caller.scope->depend({"FOO"}); + * } + * + * Synopsis: + * multiprovide(name) + * + * Synopsis: + * multidepend(list names) + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct provide { + NCDModuleInst *i; + NCDValRef name; + LinkedList1Node provides_list_node; + LinkedList1 depends_list; + int dying; +}; + +struct depend { + NCDModuleInst *i; + NCDValRef names; + LinkedList1Node depends_list_node; + struct provide *provide; + LinkedList1Node provide_depends_list_node; + int provide_collapsing; +}; + +struct global { + LinkedList1 provides_list; + LinkedList1 depends_list; +}; + +static struct provide * find_provide (struct global *g, NCDValRef name) +{ + for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->provides_list); ln; ln = LinkedList1Node_Next(ln)) { + struct provide *provide = UPPER_OBJECT(ln, struct provide, provides_list_node); + if (NCDVal_Compare(provide->name, name) == 0) { + return provide; + } + } + + return NULL; +} + +static struct provide * depend_find_best_provide (struct depend *o) +{ + struct global *g = ModuleGlobal(o->i); + + size_t count = NCDVal_ListCount(o->names); + + for (size_t j = 0; j < count; j++) { + NCDValRef name = NCDVal_ListGet(o->names, j); + struct provide *provide = find_provide(g, name); + if (provide && !provide->dying) { + return provide; + } + } + + return NULL; +} + +static void depend_update (struct depend *o) +{ + // if we're collapsing, do nothing + if (o->provide && o->provide_collapsing) { + return; + } + + // find best provide + struct provide *best_provide = depend_find_best_provide(o); + ASSERT(!best_provide || !best_provide->dying) + + // has anything changed? + if (best_provide == o->provide) { + return; + } + + if (o->provide) { + // set collapsing + o->provide_collapsing = 1; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + // insert to provide's list + LinkedList1_Append(&best_provide->depends_list, &o->provide_depends_list_node); + + // set not collapsing + o->provide_collapsing = 0; + + // set provide + o->provide = best_provide; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init provides list + LinkedList1_Init(&g->provides_list); + + // init depends list + LinkedList1_Init(&g->depends_list); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->depends_list)) + ASSERT(LinkedList1_IsEmpty(&g->provides_list)) + + // free global state structure + BFree(g); +} + +static void provide_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct provide *o = vo; + o->i = i; + + // read arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // remember name + o->name = name_arg; + + // check for existing provide with this name + if (find_provide(g, o->name)) { + ModuleLog(o->i, BLOG_ERROR, "a provide with this name already exists"); + goto fail0; + } + + // insert to provides list + LinkedList1_Append(&g->provides_list, &o->provides_list_node); + + // init depends list + LinkedList1_Init(&o->depends_list); + + // set not dying + o->dying = 0; + + // signal up. + // This comes above the loop which follows, so that effects on related depend statements are + // computed before this process advances, avoiding problems like failed variable resolutions. + NCDModuleInst_Backend_Up(o->i); + + // update depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&g->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, depends_list_node); + depend_update(depend); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void provide_free (struct provide *o) +{ + struct global *g = ModuleGlobal(o->i); + ASSERT(LinkedList1_IsEmpty(&o->depends_list)) + + // remove from provides list + LinkedList1_Remove(&g->provides_list, &o->provides_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void provide_func_die (void *vo) +{ + struct provide *o = vo; + ASSERT(!o->dying) + + // if we have no depends, die immediately + if (LinkedList1_IsEmpty(&o->depends_list)) { + provide_free(o); + return; + } + + // set dying + o->dying = 1; + + // start collapsing our depends + for (LinkedList1Node *ln = LinkedList1_GetFirst(&o->depends_list); ln; ln = LinkedList1Node_Next(ln)) { + struct depend *depend = UPPER_OBJECT(ln, struct depend, provide_depends_list_node); + ASSERT(depend->provide == o) + + // update depend to make sure it is collapsing + depend_update(depend); + } +} + +static void depend_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct depend *o = vo; + o->i = i; + + // read arguments + NCDValRef names_arg; + if (!NCDVal_ListRead(params->args, 1, &names_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(names_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // remember names + o->names = names_arg; + + // insert to depends list + LinkedList1_Append(&g->depends_list, &o->depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void depend_func_die (void *vo) +{ + struct depend *o = vo; + struct global *g = ModuleGlobal(o->i); + + if (o->provide) { + // remove from provide's list + LinkedList1_Remove(&o->provide->depends_list, &o->provide_depends_list_node); + + // if provide is dying and is empty, let it die + if (o->provide->dying && LinkedList1_IsEmpty(&o->provide->depends_list)) { + provide_free(o->provide); + } + } + + // remove from depends list + LinkedList1_Remove(&g->depends_list, &o->depends_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void depend_func_clean (void *vo) +{ + struct depend *o = vo; + + if (!(o->provide && o->provide_collapsing)) { + return; + } + + // save provide + struct provide *provide = o->provide; + + // remove from provide's list + LinkedList1_Remove(&provide->depends_list, &o->provide_depends_list_node); + + // set no provide + o->provide = NULL; + + // update + depend_update(o); + + // if provide is dying and is empty, let it die + if (provide->dying && LinkedList1_IsEmpty(&provide->depends_list)) { + provide_free(provide); + } +} + +static int depend_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct depend *o = vo; + + if (!o->provide) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->provide->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "multiprovide", + .func_new2 = provide_func_new, + .func_die = provide_func_die, + .alloc_size = sizeof(struct provide) + }, { + .type = "multidepend", + .func_new2 = depend_func_new, + .func_die = depend_func_die, + .func_clean = depend_func_clean, + .func_getobj = depend_func_getobj, + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN, + .alloc_size = sizeof(struct depend) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_multidepend = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_backend_badvpn.c b/external/badvpn_dns/ncd/modules/net_backend_badvpn.c new file mode 100644 index 00000000..572ae716 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_backend_badvpn.c @@ -0,0 +1,281 @@ +/** + * @file net_backend_badvpn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * BadVPN interface module. + * + * Synopsis: net.backend.badvpn(string ifname, string user, string exec, list(string) args) + */ + +#include +#include + +#include +#include +#include + +#include + +#define RETRY_TIME 5000 + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + const char *user; + size_t user_len; + const char *exec; + size_t exec_len; + NCDValRef args; + int dying; + int started; + BTimer timer; + BProcess process; +}; + +static void try_process (struct instance *o); +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status); +static void timer_handler (struct instance *o); +static void instance_free (struct instance *o); + +void try_process (struct instance *o) +{ + CmdLine c; + if (!CmdLine_Init(&c)) { + goto fail0; + } + + // append exec + if (!CmdLine_AppendNoNull(&c, o->exec, o->exec_len)) { + goto fail1; + } + + // append tapdev + if (!CmdLine_Append(&c, "--tapdev") || !CmdLine_Append(&c, o->ifname_nts.data)) { + goto fail1; + } + + // append arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + if (!CmdLine_AppendNoNull(&c, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + goto fail1; + } + } + + // terminate cmdline + if (!CmdLine_Finish(&c)) { + goto fail1; + } + + // start process + if (!BProcess_Init(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, ((char **)c.arr.v)[0], (char **)c.arr.v, o->user)) { + ModuleLog(o->i, BLOG_ERROR, "BProcess_Init failed"); + goto fail1; + } + + CmdLine_Free(&c); + + // set started + o->started = 1; + + return; + +fail1: + CmdLine_Free(&c); +fail0: + // retry + o->started = 0; + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->started) + + ModuleLog(o->i, BLOG_INFO, "process terminated"); + + // free process + BProcess_Free(&o->process); + + // set not started + o->started = 0; + + if (o->dying) { + instance_free(o); + return; + } + + // set timer + BReactor_SetTimer(o->i->params->iparams->reactor, &o->timer); +} + +void timer_handler (struct instance *o) +{ + ASSERT(!o->started) + + ModuleLog(o->i, BLOG_INFO, "retrying"); + + // try starting process again + try_process(o); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef user_arg; + NCDValRef exec_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &user_arg, &exec_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(user_arg) || + !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->user = NCDVal_StringData(user_arg); + o->user_len = NCDVal_StringLength(user_arg); + o->exec = NCDVal_StringData(exec_arg); + o->exec_len = NCDVal_StringLength(exec_arg); + o->args = args_arg; + + // check arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // create TAP device + if (!NCDIfConfig_make_tuntap(o->ifname_nts.data, o->user, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to create TAP device"); + goto fail1; + } + + // set device up + if (!NCDIfConfig_set_up(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set device up"); + goto fail2; + } + + // set not dying + o->dying = 0; + + // init timer + BTimer_Init(&o->timer, RETRY_TIME, (BTimer_handler)timer_handler, o); + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // try starting process + try_process(o); + return; + +fail2: + if (!NCDIfConfig_remove_tuntap(o->ifname_nts.data, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove TAP device"); + } +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(!o->started) + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + // set device down + if (!NCDIfConfig_set_down(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set device down"); + } + + // free TAP device + if (!NCDIfConfig_remove_tuntap(o->ifname_nts.data, 0)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove TAP device"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + if (!o->started) { + instance_free(o); + return; + } + + // request termination + BProcess_Terminate(&o->process); + + // remember dying + o->dying = 1; +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.badvpn", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_badvpn = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_backend_rfkill.c b/external/badvpn_dns/ncd/modules/net_backend_rfkill.c new file mode 100644 index 00000000..311d973f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_backend_rfkill.c @@ -0,0 +1,216 @@ +/** + * @file net_backend_rfkill.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Rfkill monitoring module. + * + * Synopsis: net.backend.rfkill(string type, string name) + * Arguments: + * type - method of determining the index of the rfkill device. "index" for + * rfkill device index, "wlan" for wireless device. Be aware that, for + * the wireless device method, the index is resloved at initialization, + * and no attempt is made to refresh it if the device goes away. In other + * words, you should probably put a "net.backend.waitdevice" statement + * in front of the rfkill statement. + * name - rfkill index or wireless device name + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + uint32_t index; + NCDRfkillMonitor monitor; + int up; +}; + +static int find_wlan_rfill (const char *ifname, uint32_t *out_index) +{ + char ieee_path[100]; + snprintf(ieee_path, sizeof(ieee_path), "/sys/class/net/%s/../../ieee80211", ifname); + + int res = 0; + + DIR *d = opendir(ieee_path); + if (!d) { + goto fail0; + } + + struct dirent *e; + while (e = readdir(d)) { + if (!string_begins_with(e->d_name, "phy")) { + continue; + } + + char phy_path[150]; + snprintf(phy_path, sizeof(phy_path), "%s/%s", ieee_path, e->d_name); + + DIR *d2 = opendir(phy_path); + if (!d2) { + continue; + } + + struct dirent *e2; + while (e2 = readdir(d2)) { + int index_pos; + if (!(index_pos = string_begins_with(e2->d_name, "rfkill"))) { + continue; + } + + uint32_t index; + if (sscanf(e2->d_name + index_pos, "%"SCNu32, &index) != 1) { + continue; + } + + res = 1; + *out_index = index; + } + + closedir(d2); + } + + closedir(d); +fail0: + return res; +} + +static void monitor_handler (struct instance *o, struct rfkill_event event) +{ + if (event.idx != o->index) { + return; + } + + int was_up = o->up; + o->up = (event.op != RFKILL_OP_DEL && !event.soft && !event.hard); + + if (o->up && !was_up) { + NCDModuleInst_Backend_Up(o->i); + } + else if (!o->up && was_up) { + NCDModuleInst_Backend_Down(o->i); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef type_arg; + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 2, &type_arg, &name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(type_arg) || !NCDVal_IsStringNoNulls(name_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate name + NCDValNullTermString name_nts; + if (!NCDVal_StringNullTerminate(name_arg, &name_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_StringEquals(type_arg, "index")) { + if (sscanf(name_nts.data, "%"SCNu32, &o->index) != 1) { + ModuleLog(o->i, BLOG_ERROR, "wrong index argument"); + goto fail1; + } + } + else if (NCDVal_StringEquals(type_arg, "wlan")) { + if (!find_wlan_rfill(name_nts.data, &o->index)) { + ModuleLog(o->i, BLOG_ERROR, "failed to find rfkill for wlan interface"); + goto fail1; + } + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown type argument"); + goto fail1; + } + + // init monitor + if (!NCDRfkillMonitor_Init(&o->monitor, o->i->params->iparams->reactor, (NCDRfkillMonitor_handler)monitor_handler, o)) { + ModuleLog(o->i, BLOG_ERROR, "monitor failed"); + goto fail1; + } + + // set not up + o->up = 0; + + // free name nts + NCDValNullTermString_Free(&name_nts); + return; + +fail1: + NCDValNullTermString_Free(&name_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free monitor + NCDRfkillMonitor_Free(&o->monitor); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.rfkill", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_rfkill = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_backend_waitdevice.c b/external/badvpn_dns/ncd/modules/net_backend_waitdevice.c new file mode 100644 index 00000000..6ed99f6f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_backend_waitdevice.c @@ -0,0 +1,187 @@ +/** + * @file net_backend_waitdevice.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which waits for the presence of a network interface. + * + * Synopsis: net.backend.waitdevice(string ifname) + * Description: statement is UP when a network interface named ifname + * exists, and DOWN when it does not. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define DEVPATH_REGEX "/net/[^/]+$" + +struct instance { + NCDModuleInst *i; + const char *ifname; + size_t ifname_len; + NCDUdevClient client; + regex_t reg; + char *devpath; + uintmax_t ifindex; +}; + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + if (o->devpath && !strcmp(devpath, o->devpath) && !NCDUdevManager_Query(o->i->params->iparams->umanager, o->devpath)) { + // free devpath + free(o->devpath); + + // set no devpath + o->devpath = NULL; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } else { + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + if (!cache_map) { + goto out; + } + + int match_res = regexec(&o->reg, devpath, 0, NULL, 0); + const char *interface = BStringMap_Get(cache_map, "INTERFACE"); + const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX"); + + uintmax_t ifindex; + if (!(!match_res && interface && strlen(interface) == o->ifname_len && !memcmp(interface, o->ifname, o->ifname_len) && ifindex_str && parse_unsigned_integer(ifindex_str, &ifindex))) { + goto out; + } + + if (o->devpath && (strcmp(o->devpath, devpath) || o->ifindex != ifindex)) { + // free devpath + free(o->devpath); + + // set no devpath + o->devpath = NULL; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + if (!o->devpath) { + // grab devpath + o->devpath = devpath; + devpath = NULL; + + // remember ifindex + o->ifindex = ifindex; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef arg; + if (!NCDVal_ListRead(params->args, 1, &arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->ifname = NCDVal_StringData(arg); + o->ifname_len = NCDVal_StringLength(arg); + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // compile regex + if (regcomp(&o->reg, DEVPATH_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail1; + } + + // set no devpath + o->devpath = NULL; + return; + +fail1: + NCDUdevClient_Free(&o->client); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free devpath + if (o->devpath) { + free(o->devpath); + } + + // free regex + regfree(&o->reg); + + // free client + NCDUdevClient_Free(&o->client); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.waitdevice", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_waitdevice = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_backend_waitlink.c b/external/badvpn_dns/ncd/modules/net_backend_waitlink.c new file mode 100644 index 00000000..4ea54e8d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_backend_waitlink.c @@ -0,0 +1,155 @@ +/** + * @file net_backend_waitlink.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which waits for the link on a network interface. + * + * Synopsis: net.backend.waitlink(string ifname) + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDInterfaceMonitor monitor; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_event event) +{ + ASSERT(event.event == NCDIFMONITOR_EVENT_LINK_UP || event.event == NCDIFMONITOR_EVENT_LINK_DOWN) + + int was_up = o->up; + o->up = (event.event == NCDIFMONITOR_EVENT_LINK_UP); + + if (o->up && !was_up) { + NCDModuleInst_Backend_Up(o->i); + } + else if (!o->up && was_up) { + NCDModuleInst_Backend_Down(o->i); + } +} + +static void monitor_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "monitor error"); + + instance_free(o, 1); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // get interface index + int ifindex; + int res = badvpn_get_iface_info(ifname_nts.data, NULL, NULL, &ifindex); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to get interface index"); + goto fail0; + } + + // init monitor + if (!NCDInterfaceMonitor_Init(&o->monitor, ifindex, NCDIFMONITOR_WATCH_LINK, i->params->iparams->reactor, o, (NCDInterfaceMonitor_handler)monitor_handler, (NCDInterfaceMonitor_handler_error)monitor_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed"); + goto fail0; + } + + // set not up + o->up = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free monitor + NCDInterfaceMonitor_Free(&o->monitor); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.waitlink", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_waitlink = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_backend_wpa_supplicant.c b/external/badvpn_dns/ncd/modules/net_backend_wpa_supplicant.c new file mode 100644 index 00000000..ce72198a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_backend_wpa_supplicant.c @@ -0,0 +1,573 @@ +/** + * @file net_backend_wpa_supplicant.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Wireless interface module which runs wpa_supplicant to connect to a wireless network. + * + * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to + * start if the switch is of when it is started, and will stop working indefinitely if the + * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill" + * statement in front of the wpa_supplicant statement. + * + * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args) + * Variables: + * bssid - BSSID of the wireless network we connected to, or "none". + * Consists of 6 capital, two-character hexadecimal numbers, separated with colons. + * Example: "01:B2:C3:04:E5:F6" + * ssid - SSID of the wireless network we connected to. Note that this is after what + * wpa_supplicant does to it before it prints it. In particular, it replaces all bytes + * outside [32, 126] with underscores. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_LINE_LEN 512 +#define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED" +#define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED" + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + const char *ifname; + size_t ifname_len; + const char *conf; + size_t conf_len; + const char *exec; + size_t exec_len; + NCDValRef args; + int dying; + int up; + BInputProcess process; + int have_pipe; + LineBuffer pipe_buffer; + PacketPassInterface pipe_input; + int have_info; + int info_have_bssid; + uint8_t info_bssid[6]; + char *info_ssid; +}; + +static int parse_hex_digit (uint8_t d, uint8_t *out); +static int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len); +static int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len); +static int build_cmdline (struct instance *o, CmdLine *c); +static int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len); +static void free_info (struct instance *o); +static void process_error (struct instance *o); +static void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status); +static void process_handler_closed (struct instance *o, int is_error); +static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len); +static void instance_free (struct instance *o, int is_error); + +int parse_hex_digit (uint8_t d, uint8_t *out) +{ + switch (d) { + case '0': *out = 0; return 1; + case '1': *out = 1; return 1; + case '2': *out = 2; return 1; + case '3': *out = 3; return 1; + case '4': *out = 4; return 1; + case '5': *out = 5; return 1; + case '6': *out = 6; return 1; + case '7': *out = 7; return 1; + case '8': *out = 8; return 1; + case '9': *out = 9; return 1; + case 'A': case 'a': *out = 10; return 1; + case 'B': case 'b': *out = 11; return 1; + case 'C': case 'c': *out = 12; return 1; + case 'D': case 'd': *out = 13; return 1; + case 'E': case 'e': *out = 14; return 1; + case 'F': case 'f': *out = 15; return 1; + } + + return 0; +} + +int parse_trying (uint8_t *data, int data_len, uint8_t *out_bssid, uint8_t **out_ssid, int *out_ssid_len) +{ + // Trying to associate with AB:CD:EF:01:23:45 (SSID='Some SSID' freq=2462 MHz) + + int p; + if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with "))) { + return 0; + } + data += p; + data_len -= p; + + for (int i = 0; i < 6; i++) { + uint8_t d1; + uint8_t d2; + if (data_len < 2 || !parse_hex_digit(data[0], &d1) || !parse_hex_digit(data[1], &d2)) { + return 0; + } + data += 2; + data_len -= 2; + out_bssid[i] = ((d1 << 4) | d2); + + if (i != 5) { + if (data_len < 1 || data[0] != ':') { + return 0; + } + data += 1; + data_len -= 1; + } + } + + if (!(p = data_begins_with((char *)data, data_len, " (SSID='"))) { + return 0; + } + data += p; + data_len -= p; + + // find last ' + uint8_t *q = NULL; + for (int i = data_len; i > 0; i--) { + if (data[i - 1] == '\'') { + q = &data[i - 1]; + break; + } + } + if (!q) { + return 0; + } + + *out_ssid = data; + *out_ssid_len = q - data; + + return 1; +} + +int parse_trying_nobssid (uint8_t *data, int data_len, uint8_t **out_ssid, int *out_ssid_len) +{ + // Trying to associate with SSID 'Some SSID' + + int p; + if (!(p = data_begins_with((char *)data, data_len, "Trying to associate with SSID '"))) { + return 0; + } + data += p; + data_len -= p; + + // find last ' + uint8_t *q = NULL; + for (int i = data_len; i > 0; i--) { + if (data[i - 1] == '\'') { + q = &data[i - 1]; + break; + } + } + if (!q) { + return 0; + } + + *out_ssid = data; + *out_ssid_len = q - data; + + return 1; +} + +int build_cmdline (struct instance *o, CmdLine *c) +{ + // init cmdline + if (!CmdLine_Init(c)) { + goto fail0; + } + + // find stdbuf executable + char *stdbuf_exec = badvpn_find_program("stdbuf"); + if (!stdbuf_exec) { + ModuleLog(o->i, BLOG_ERROR, "cannot find stdbuf executable"); + goto fail1; + } + + // append stdbuf part + int res = build_stdbuf_cmdline(c, stdbuf_exec, o->exec, o->exec_len); + free(stdbuf_exec); + if (!res) { + goto fail1; + } + + // append user arguments + size_t count = NCDVal_ListCount(o->args); + for (size_t j = 0; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(o->args, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // append argument + if (!CmdLine_AppendNoNull(c, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + goto fail1; + } + } + + // append interface name + if (!CmdLine_Append(c, "-i") || !CmdLine_AppendNoNull(c, o->ifname, o->ifname_len)) { + goto fail1; + } + + // append config file + if (!CmdLine_Append(c, "-c") || !CmdLine_AppendNoNull(c, o->conf, o->conf_len)) { + goto fail1; + } + + // terminate cmdline + if (!CmdLine_Finish(c)) { + goto fail1; + } + + return 1; + +fail1: + CmdLine_Free(c); +fail0: + return 0; +} + +int init_info (struct instance *o, int have_bssid, const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len) +{ + ASSERT(!o->have_info) + + // set bssid + o->info_have_bssid = have_bssid; + if (have_bssid) { + memcpy(o->info_bssid, bssid, 6); + } + + // set ssid + if (!(o->info_ssid = BAllocSize(bsize_add(bsize_fromsize(ssid_len), bsize_fromsize(1))))) { + ModuleLog(o->i, BLOG_ERROR, "BAllocSize failed"); + return 0; + } + memcpy(o->info_ssid, ssid, ssid_len); + o->info_ssid[ssid_len] = '\0'; + + // set have info + o->have_info = 1; + + return 1; +} + +void free_info (struct instance *o) +{ + ASSERT(o->have_info) + + // free ssid + BFree(o->info_ssid); + + // set not have info + o->have_info = 0; +} + +void process_error (struct instance *o) +{ + BInputProcess_Terminate(&o->process); +} + +void process_handler_terminated (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated"); + + // die + instance_free(o, !o->dying); + return; +} + +void process_handler_closed (struct instance *o, int is_error) +{ + ASSERT(o->have_pipe) + + if (is_error) { + ModuleLog(o->i, BLOG_ERROR, "pipe error"); + } else { + ModuleLog(o->i, BLOG_INFO, "pipe closed"); + } + + // free buffer + LineBuffer_Free(&o->pipe_buffer); + + // free input interface + PacketPassInterface_Free(&o->pipe_input); + + // set have no pipe + o->have_pipe = 0; +} + +void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len) +{ + ASSERT(o->have_pipe) + ASSERT(data_len > 0) + + // accept packet + PacketPassInterface_Done(&o->pipe_input); + + if (o->dying) { + return; + } + + // strip "interface: " from beginning of line. Older wpa_supplicant versions (<1.0) don't add this + // prefix, so don't fail if there isn't one. + size_t l1; + size_t l2; + if (o->ifname_len > 0 && (l1 = data_begins_with_bin((char *)data, data_len, o->ifname, o->ifname_len)) && (l2 = data_begins_with((char *)data + l1, data_len - l1, ": "))) { + data += l1 + l2; + data_len -= l1 + l2; + } + + int have_bssid = 1; + uint8_t bssid[6]; + uint8_t *ssid; + int ssid_len; + if (parse_trying(data, data_len, bssid, &ssid, &ssid_len) || (have_bssid = 0, parse_trying_nobssid(data, data_len, &ssid, &ssid_len))) { + ModuleLog(o->i, BLOG_INFO, "trying event"); + + if (o->up) { + ModuleLog(o->i, BLOG_ERROR, "trying unexpected!"); + process_error(o); + return; + } + + if (o->have_info) { + free_info(o); + } + + if (!init_info(o, have_bssid, bssid, ssid, ssid_len)) { + ModuleLog(o->i, BLOG_ERROR, "init_info failed"); + process_error(o); + return; + } + } + else if (data_begins_with((char *)data, data_len, EVENT_STRING_CONNECTED)) { + ModuleLog(o->i, BLOG_INFO, "connected event"); + + if (o->up || !o->have_info) { + ModuleLog(o->i, BLOG_ERROR, "connected unexpected!"); + process_error(o); + return; + } + + o->up = 1; + NCDModuleInst_Backend_Up(o->i); + } + else if (data_begins_with((char *)data, data_len, EVENT_STRING_DISCONNECTED)) { + ModuleLog(o->i, BLOG_INFO, "disconnected event"); + + if (o->have_info) { + free_info(o); + } + + if (o->up) { + o->up = 0; + NCDModuleInst_Backend_Down(o->i); + } + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef conf_arg; + NCDValRef exec_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsStringNoNulls(conf_arg) || + !NCDVal_IsStringNoNulls(exec_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->ifname = NCDVal_StringData(ifname_arg); + o->ifname_len = NCDVal_StringLength(ifname_arg); + o->conf = NCDVal_StringData(conf_arg); + o->conf_len = NCDVal_StringLength(conf_arg); + o->exec = NCDVal_StringData(exec_arg); + o->exec_len = NCDVal_StringLength(exec_arg); + o->args = args_arg; + + // set not dying + o->dying = 0; + + // set not up + o->up = 0; + + // build process cmdline + CmdLine c; + if (!build_cmdline(o, &c)) { + ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline"); + goto fail0; + } + + // init process + if (!BInputProcess_Init(&o->process, o->i->params->iparams->reactor, o->i->params->iparams->manager, o, + (BInputProcess_handler_terminated)process_handler_terminated, + (BInputProcess_handler_closed)process_handler_closed + )) { + ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Init failed"); + goto fail1; + } + + // init input interface + PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->params->iparams->reactor)); + + // init buffer + if (!LineBuffer_Init(&o->pipe_buffer, BInputProcess_GetInput(&o->process), &o->pipe_input, MAX_LINE_LEN, '\n')) { + ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed"); + goto fail2; + } + + // set have pipe + o->have_pipe = 1; + + // start process + if (!BInputProcess_Start(&o->process, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL)) { + ModuleLog(o->i, BLOG_ERROR, "BInputProcess_Start failed"); + goto fail3; + } + + // set not have info + o->have_info = 0; + + CmdLine_Free(&c); + return; + +fail3: + LineBuffer_Free(&o->pipe_buffer); +fail2: + PacketPassInterface_Free(&o->pipe_input); + BInputProcess_Free(&o->process); +fail1: + CmdLine_Free(&c); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // free info + if (o->have_info) { + free_info(o); + } + + if (o->have_pipe) { + // free buffer + LineBuffer_Free(&o->pipe_buffer); + + // free input interface + PacketPassInterface_Free(&o->pipe_input); + } + + // free process + BInputProcess_Free(&o->process); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // request termination + BInputProcess_Terminate(&o->process); + + // remember dying + o->dying = 1; +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + ASSERT(o->have_info) + + if (!strcmp(name, "bssid")) { + char str[18]; + + if (!o->info_have_bssid) { + sprintf(str, "none"); + } else { + uint8_t *id = o->info_bssid; + sprintf(str, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8, id[0], id[1], id[2], id[3], id[4], id[5]); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "ssid")) { + *out = NCDVal_NewString(mem, o->info_ssid); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.backend.wpa_supplicant", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_dns.c b/external/badvpn_dns/ncd/modules/net_dns.c new file mode 100644 index 00000000..9ecdf1c3 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_dns.c @@ -0,0 +1,434 @@ +/** + * @file net_dns.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DNS servers module. + * + * Synopsis: net.dns(list(string) servers, string priority) + * Synopsis: net.dns.resolvconf(list({string type, string value}) lines, string priority) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +struct instance { + NCDModuleInst *i; + LinkedList1 entries; + LinkedList1Node instances_node; // node in instances +}; + +struct dns_entry { + LinkedList1Node list_node; // node in instance.entries + char *line; + int priority; +}; + +struct global { + LinkedList1 instances; +}; + +static struct dns_entry * add_dns_entry (struct instance *o, const char *type, const char *value, int priority) +{ + // allocate entry + struct dns_entry *entry = malloc(sizeof(*entry)); + if (!entry) { + goto fail0; + } + + // generate line + entry->line = concat_strings(4, type, " ", value, "\n"); + if (!entry->line) { + goto fail1; + } + + // set info + entry->priority = priority; + + // add to list + LinkedList1_Append(&o->entries, &entry->list_node); + + return entry; + +fail1: + free(entry); +fail0: + return NULL; +} + +static void remove_dns_entry (struct instance *o, struct dns_entry *entry) +{ + // remove from list + LinkedList1_Remove(&o->entries, &entry->list_node); + + // free line + free(entry->line); + + // free entry + free(entry); +} + +static void remove_entries (struct instance *o) +{ + LinkedList1Node *n; + while (n = LinkedList1_GetFirst(&o->entries)) { + struct dns_entry *e = UPPER_OBJECT(n, struct dns_entry, list_node); + remove_dns_entry(o, e); + } +} + +static size_t count_entries (struct global *g) +{ + size_t c = 0; + + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) { + struct instance *o = UPPER_OBJECT(n, struct instance, instances_node); + for (LinkedList1Node *en = LinkedList1_GetFirst(&o->entries); en; en = LinkedList1Node_Next(en)) { + c++; + } + } + + return c; +} + +struct dns_sort_entry { + char *line; + int priority; +}; + +static int dns_sort_comparator (const void *v1, const void *v2) +{ + const struct dns_sort_entry *e1 = v1; + const struct dns_sort_entry *e2 = v2; + return B_COMPARE(e1->priority, e2->priority); +} + +static int set_servers (struct global *g) +{ + int ret = 0; + + // count servers + size_t num_entries = count_entries(g); + + // allocate sort array + struct dns_sort_entry *sort_entries = BAllocArray(num_entries, sizeof(sort_entries[0])); + if (!sort_entries) { + goto fail0; + } + + // fill sort array + num_entries = 0; + for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) { + struct instance *o = UPPER_OBJECT(n, struct instance, instances_node); + for (LinkedList1Node *en = LinkedList1_GetFirst(&o->entries); en; en = LinkedList1Node_Next(en)) { + struct dns_entry *e = UPPER_OBJECT(en, struct dns_entry, list_node); + sort_entries[num_entries].line = e->line; + sort_entries[num_entries].priority= e->priority; + num_entries++; + } + } + + // sort by priority + // use a custom insertion sort instead of qsort() because we want a stable sort + struct dns_sort_entry temp; + BInsertionSort(sort_entries, num_entries, sizeof(sort_entries[0]), dns_sort_comparator, &temp); + + ExpString estr; + if (!ExpString_Init(&estr)) { + goto fail1; + } + + for (size_t i = 0; i < num_entries; i++) { + if (!ExpString_Append(&estr, sort_entries[i].line)) { + goto fail2; + } + } + + // set servers + if (!NCDIfConfig_set_resolv_conf(ExpString_Get(&estr), ExpString_Length(&estr))) { + goto fail2; + } + + ret = 1; + +fail2: + ExpString_Free(&estr); +fail1: + BFree(sort_entries); +fail0: + return ret; +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init instances list + LinkedList1_Init(&g->instances); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + ASSERT(LinkedList1_IsEmpty(&g->instances)) + + // free global state structure + BFree(g); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct instance *o = vo; + o->i = i; + + // init servers list + LinkedList1_Init(&o->entries); + + // get arguments + NCDValRef servers_arg; + NCDValRef priority_arg; + if (!NCDVal_ListRead(params->args, 2, &servers_arg, &priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsList(servers_arg) || !NCDVal_IsString(priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + uintmax_t priority; + if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong priority"); + goto fail1; + } + + // read servers + size_t count = NCDVal_ListCount(servers_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef server_arg = NCDVal_ListGet(servers_arg, j); + + if (!NCDVal_IsString(server_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(server_arg), NCDVal_StringLength(server_arg), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong addr"); + goto fail1; + } + + char addr_str[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr, addr_str); + + if (!add_dns_entry(o, "nameserver", addr_str, priority)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry"); + goto fail1; + } + } + + // add to instances + LinkedList1_Append(&g->instances, &o->instances_node); + + // set servers + if (!set_servers(g)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers"); + goto fail2; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail2: + LinkedList1_Remove(&g->instances, &o->instances_node); +fail1: + remove_entries(o); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_resolvconf (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct instance *o = vo; + o->i = i; + + // init servers list + LinkedList1_Init(&o->entries); + + // get arguments + NCDValRef lines_arg; + NCDValRef priority_arg; + if (!NCDVal_ListRead(params->args, 2, &lines_arg, &priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsList(lines_arg) || !NCDVal_IsString(priority_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + uintmax_t priority; + if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong priority"); + goto fail1; + } + + // read lines + size_t count = NCDVal_ListCount(lines_arg); + for (size_t j = 0; j < count; j++) { + int loop_failed = 1; + + NCDValRef line = NCDVal_ListGet(lines_arg, j); + if (!NCDVal_IsList(line) || NCDVal_ListCount(line) != 2) { + ModuleLog(o->i, BLOG_ERROR, "lines element is not a list with two elements"); + goto loop_fail0; + } + + NCDValRef type = NCDVal_ListGet(line, 0); + NCDValRef value = NCDVal_ListGet(line, 1); + if (!NCDVal_IsStringNoNulls(type) || !NCDVal_IsStringNoNulls(value)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type of type or value"); + goto loop_fail0; + } + + NCDValNullTermString type_nts; + if (!NCDVal_StringNullTerminate(type, &type_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto loop_fail0; + } + + NCDValNullTermString value_nts; + if (!NCDVal_StringNullTerminate(value, &value_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto loop_fail1; + } + + if (!add_dns_entry(o, type_nts.data, value_nts.data, priority)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry"); + goto loop_fail2; + } + + loop_failed = 0; + loop_fail2: + NCDValNullTermString_Free(&value_nts); + loop_fail1: + NCDValNullTermString_Free(&type_nts); + loop_fail0: + if (loop_failed) { + goto fail1; + } + } + + // add to instances + LinkedList1_Append(&g->instances, &o->instances_node); + + // set servers + if (!set_servers(g)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers"); + goto fail2; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail2: + LinkedList1_Remove(&g->instances, &o->instances_node); +fail1: + remove_entries(o); + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + struct global *g = ModuleGlobal(o->i); + + // remove from instances + LinkedList1_Remove(&g->instances, &o->instances_node); + + // set servers + set_servers(g); + + // free servers + remove_entries(o); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.dns", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.dns.resolvconf", + .func_new2 = func_new_resolvconf, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_dns = { + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree, + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_iptables.c b/external/badvpn_dns/ncd/modules/net_iptables.c new file mode 100644 index 00000000..a5af2eea --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_iptables.c @@ -0,0 +1,749 @@ +/** + * @file net_iptables.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * iptables and ebtables module. + * + * Note that all iptables/ebtables commands (in general) must be issued synchronously, or + * the kernel may randomly report errors if there is another iptables/ebtables command in + * progress. To solve this, the NCD process contains a single "iptables lock". All + * iptables/ebtables commands exposed here go through that lock. + * In case you wish to call iptables/ebtables directly, the lock is exposed via + * net.iptables.lock(). + * + * The append and insert commands, instead of using the variable-argument form below + * as documented below, may alternatively be called with a single list argument. + * + * Synopsis: + * net.iptables.append(string table, string chain, string arg1 ...) + * Description: + * init: iptables -t table -A chain arg1 ... + * deinit: iptables -t table -D chain arg1 ... + * + * Synopsis: + * net.iptables.insert(string table, string chain, string arg1 ...) + * Description: + * init: iptables -t table -I chain arg1 ... + * deinit: iptables -t table -D chain arg1 ... + * + * Synopsis: + * net.iptables.policy(string table, string chain, string target, string revert_target) + * Description: + * init: iptables -t table -P chain target + * deinit: iptables -t table -P chain revert_target + * + * Synopsis: + * net.iptables.newchain(string table, string chain) + * net.iptables.newchain(string chain) // DEPRECATED, defaults to table="filter" + * Description: + * init: iptables -t table -N chain + * deinit: iptables -t table -X chain + * + * Synopsis: + * net.ebtables.append(string table, string chain, string arg1 ...) + * Description: + * init: ebtables -t table -A chain arg1 ... + * deinit: ebtables -t table -D chain arg1 ... + * + * Synopsis: + * net.ebtables.insert(string table, string chain, string arg1 ...) + * Description: + * init: ebtables -t table -I chain arg1 ... + * deinit: ebtables -t table -D chain arg1 ... + * + * Synopsis: + * net.ebtables.policy(string table, string chain, string target, string revert_target) + * Description: + * init: ebtables -t table -P chain target + * deinit: ebtables -t table -P chain revert_target + * + * Synopsis: + * net.ebtables.newchain(string table, string chain) + * Description: + * init: ebtables -t table -N chain + * deinit: ebtables -t table -X chain + * + * Synopsis: + * net.iptables.lock() + * Description: + * Use at the beginning of a block of custom iptables/ebtables commands to make sure + * they do not interfere with other iptables/ebtables commands. + * WARNING: improper usage of the lock can lead to deadlock. In particular: + * - Do not call any of the iptables/ebtables wrappers above from a lock section; + * those will attempt to aquire the lock themselves. + * - Do not enter another lock section from a lock section. + * - Do not perform any potentially long wait from a lock section. + * + * Synopsis: + * net.iptables.lock::unlock() + * Description: + * Use at the end of a block of custom iptables/ebtables commands to make sure + * they do not interfere with other iptables/ebtables commands. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleGlobal(i) ((i)->m->group->group_state) + +static void template_free_func (void *vo, int is_error); + +struct global { + BEventLock iptables_lock; +}; + +struct instance { + NCDModuleInst *i; + command_template_instance cti; +}; + +struct unlock_instance; + +#define LOCK_STATE_LOCKING 1 +#define LOCK_STATE_LOCKED 2 +#define LOCK_STATE_UNLOCKED 3 +#define LOCK_STATE_RELOCKING 4 + +struct lock_instance { + NCDModuleInst *i; + BEventLockJob lock_job; + struct unlock_instance *unlock; + int state; +}; + +struct unlock_instance { + NCDModuleInst *i; + struct lock_instance *lock; +}; + +static void unlock_free (struct unlock_instance *o); + +static int build_append_or_insert_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl, const char *type) +{ + if (NCDVal_ListRead(args, 1, &args) && !NCDVal_IsList(args)) { + ModuleLog(i, BLOG_ERROR, "in one-argument form a list is expected"); + goto fail0; + } + + // read arguments + NCDValRef table_arg; + NCDValRef chain_arg; + if (!NCDVal_ListReadHead(args, 2, &table_arg, &chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(table_arg) || !NCDVal_IsStringNoNulls(chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = NCDVal_StringData(table_arg); + size_t table_len = NCDVal_StringLength(table_arg); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec) || !CmdLine_Append(cl, "-t") || !CmdLine_AppendNoNull(cl, table, table_len) || !CmdLine_Append(cl, (remove ? "-D" : type)) || !CmdLine_AppendNoNull(cl, chain, chain_len)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + size_t count = NCDVal_ListCount(args); + for (size_t j = 2; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(args, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + if (!CmdLine_AppendNoNull(cl, NCDVal_StringData(arg), NCDVal_StringLength(arg))) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendNoNull failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_append_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + return build_append_or_insert_cmdline(i, args, prog, remove, exec, cl, "-A"); +} + +static int build_insert_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + return build_append_or_insert_cmdline(i, args, prog, remove, exec, cl, "-I"); +} + +static int build_policy_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef table_arg; + NCDValRef chain_arg; + NCDValRef target_arg; + NCDValRef revert_target_arg; + if (!NCDVal_ListRead(args, 4, &table_arg, &chain_arg, &target_arg, &revert_target_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(table_arg) || !NCDVal_IsStringNoNulls(chain_arg) || + !NCDVal_IsStringNoNulls(target_arg) || !NCDVal_IsStringNoNulls(revert_target_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = NCDVal_StringData(table_arg); + size_t table_len = NCDVal_StringLength(table_arg); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + const char *target = NCDVal_StringData(target_arg); + size_t target_len = NCDVal_StringLength(target_arg); + const char *revert_target = NCDVal_StringData(revert_target_arg); + size_t revert_target_len = NCDVal_StringLength(revert_target_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add arguments + if (!CmdLine_Append(cl, *exec) || !CmdLine_Append(cl, "-t") || !CmdLine_AppendNoNull(cl, table, table_len) || + !CmdLine_Append(cl, "-P") || !CmdLine_AppendNoNull(cl, chain, chain_len) || + !CmdLine_AppendNoNull(cl, (remove ? revert_target : target), (remove ? revert_target_len : target_len)) + ) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_newchain_cmdline (NCDModuleInst *i, NCDValRef args, const char *prog, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef table_arg = NCDVal_NewInvalid(); + NCDValRef chain_arg; + if (!NCDVal_ListRead(args, 1, &chain_arg) && !NCDVal_ListRead(args, 2, &table_arg, &chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if ((!NCDVal_IsInvalid(table_arg) && !NCDVal_IsStringNoNulls(table_arg)) || !NCDVal_IsStringNoNulls(chain_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + const char *table = (NCDVal_IsInvalid(table_arg) ? "filter" : NCDVal_StringData(table_arg)); + size_t table_len = (NCDVal_IsInvalid(table_arg) ? 6 : NCDVal_StringLength(table_arg)); + const char *chain = NCDVal_StringData(chain_arg); + size_t chain_len = NCDVal_StringLength(chain_arg); + + // find program + if (!(*exec = badvpn_find_program(prog))) { + ModuleLog(i, BLOG_ERROR, "failed to find program: %s", prog); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add arguments + if (!CmdLine_AppendMulti(cl, 2, *exec, "-t") || + !CmdLine_AppendNoNull(cl, table, table_len) || + !CmdLine_Append(cl, (remove ? "-X" : "-N")) || + !CmdLine_AppendNoNull(cl, chain, chain_len) + ) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static int build_iptables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_iptables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "iptables", remove, exec, cl); +} + +static int build_ip6tables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ip6tables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "ip6tables", remove, exec, cl); +} + +static int build_ebtables_append_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_append_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_insert_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_insert_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_policy_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_policy_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static int build_ebtables_newchain_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + return build_newchain_cmdline(i, args, "ebtables", remove, exec, cl); +} + +static void lock_job_handler (struct lock_instance *o) +{ + ASSERT(o->state == LOCK_STATE_LOCKING || o->state == LOCK_STATE_RELOCKING) + + if (o->state == LOCK_STATE_LOCKING) { + ASSERT(!o->unlock) + + // up + NCDModuleInst_Backend_Up(o->i); + + // set state locked + o->state = LOCK_STATE_LOCKED; + } + else if (o->state == LOCK_STATE_RELOCKING) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + + // die unlock + unlock_free(o->unlock); + o->unlock = NULL; + + // set state locked + o->state = LOCK_STATE_LOCKED; + } +} + +static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params) +{ + // allocate global state structure + struct global *g = BAlloc(sizeof(*g)); + if (!g) { + BLog(BLOG_ERROR, "BAlloc failed"); + return 0; + } + + // set group state pointer + group->group_state = g; + + // init iptables lock + BEventLock_Init(&g->iptables_lock, BReactor_PendingGroup(params->reactor)); + + return 1; +} + +static void func_globalfree (struct NCDInterpModuleGroup *group) +{ + struct global *g = group->group_state; + + // free iptables lock + BEventLock_Free(&g->iptables_lock); + + // free global state structure + BFree(g); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, command_template_build_cmdline build_cmdline) +{ + struct global *g = ModuleGlobal(i); + struct instance *o = vo; + o->i = i; + + command_template_new(&o->cti, i, params, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &g->iptables_lock); +} + +void template_free_func (void *vo, int is_error) +{ + struct instance *o = vo; + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void append_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_append_cmdline); +} + +static void insert_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_insert_cmdline); +} + +static void policy_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_policy_cmdline); +} + +static void newchain_iptables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_iptables_newchain_cmdline); +} + +static void append_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_append_cmdline); +} + +static void insert_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_insert_cmdline); +} + +static void policy_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_policy_cmdline); +} + +static void newchain_ip6tables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ip6tables_newchain_cmdline); +} + +static void append_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_append_cmdline); +} + +static void insert_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_insert_cmdline); +} + +static void policy_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_policy_cmdline); +} + +static void newchain_ebtables_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new(vo, i, params, build_ebtables_newchain_cmdline); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + command_template_die(&o->cti); +} + +static void lock_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct global *g = ModuleGlobal(i); + struct lock_instance *o = vo; + o->i = i; + + // init lock job + BEventLockJob_Init(&o->lock_job, &g->iptables_lock, (BEventLock_handler)lock_job_handler, o); + BEventLockJob_Wait(&o->lock_job); + + // set no unlock + o->unlock = NULL; + + // set state locking + o->state = LOCK_STATE_LOCKING; +} + +static void lock_func_die (void *vo) +{ + struct lock_instance *o = vo; + + if (o->state == LOCK_STATE_UNLOCKED) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + o->unlock->lock = NULL; + } + else if (o->state == LOCK_STATE_RELOCKING) { + ASSERT(o->unlock) + ASSERT(o->unlock->lock == o) + unlock_free(o->unlock); + } + else { + ASSERT(!o->unlock) + } + + // free lock job + BEventLockJob_Free(&o->lock_job); + + // dead + NCDModuleInst_Backend_Dead(o->i); +} + +static void unlock_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct unlock_instance *o = vo; + o->i = i; + + // get lock lock + struct lock_instance *lock = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure lock doesn't already have an unlock + if (lock->unlock) { + BLog(BLOG_ERROR, "lock already has an unlock"); + goto fail0; + } + + // make sure lock is locked + if (lock->state != LOCK_STATE_LOCKED) { + BLog(BLOG_ERROR, "lock is not locked"); + goto fail0; + } + + // set lock + o->lock = lock; + + // set unlock in lock + lock->unlock = o; + + // up + NCDModuleInst_Backend_Up(o->i); + + // release lock + BEventLockJob_Release(&lock->lock_job); + + // set lock state unlocked + lock->state = LOCK_STATE_UNLOCKED; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void unlock_func_die (void *vo) +{ + struct unlock_instance *o = vo; + + // if lock is gone, die right away + if (!o->lock) { + unlock_free(o); + return; + } + + ASSERT(o->lock->unlock == o) + ASSERT(o->lock->state == LOCK_STATE_UNLOCKED) + + // wait lock + BEventLockJob_Wait(&o->lock->lock_job); + + // set lock state relocking + o->lock->state = LOCK_STATE_RELOCKING; +} + +static void unlock_free (struct unlock_instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.iptables.append", + .func_new2 = append_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.insert", + .func_new2 = insert_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.policy", + .func_new2 = policy_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.newchain", + .func_new2 = newchain_iptables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.append", + .func_new2 = append_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.insert", + .func_new2 = insert_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.policy", + .func_new2 = policy_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ip6tables.newchain", + .func_new2 = newchain_ip6tables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.append", + .func_new2 = append_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.insert", + .func_new2 = insert_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.policy", + .func_new2 = policy_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ebtables.newchain", + .func_new2 = newchain_ebtables_func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.iptables.lock", + .func_new2 = lock_func_new, + .func_die = lock_func_die, + .alloc_size = sizeof(struct lock_instance) + }, { + .type = "net.iptables.lock::unlock", + .func_new2 = unlock_func_new, + .func_die = unlock_func_die, + .alloc_size = sizeof(struct unlock_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_iptables = { + .modules = modules, + .func_globalinit = func_globalinit, + .func_globalfree = func_globalfree +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv4_addr.c b/external/badvpn_dns/ncd/modules/net_ipv4_addr.c new file mode 100644 index 00000000..14eaea4a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv4_addr.c @@ -0,0 +1,148 @@ +/** + * @file net_ipv4_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv4 address module. + * + * Synopsis: + * net.ipv4.addr(string ifname, string addr, string prefix) + * net.ipv4.addr(string ifname, string cidr_addr) + * + * Description: + * Adds the given address to the given network interface on initialization, + * and removes it on deinitialization. The second form takes the address and + * prefix in CIDR notation (a.b.c.d/n). + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + struct ipv4_ifaddr ifaddr; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef addr_arg; + NCDValRef prefix_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &ifname_arg, &addr_arg) && + !NCDVal_ListRead(params->args, 3, &ifname_arg, &addr_arg, &prefix_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsString(addr_arg) || + (!NCDVal_IsInvalid(prefix_arg) && !NCDVal_IsString(prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_IsInvalid(prefix_arg)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation address"); + goto fail1; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail1; + } + + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &o->ifaddr.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong prefix"); + goto fail1; + } + } + + // add address + if (!NCDIfConfig_add_ipv4_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add IP address"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove address + if (!NCDIfConfig_remove_ipv4_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove IP address"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.addr", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_addr = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv4_addr_in_network.c b/external/badvpn_dns/ncd/modules/net_ipv4_addr_in_network.c new file mode 100644 index 00000000..fa63811b --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv4_addr_in_network.c @@ -0,0 +1,173 @@ +/** + * @file net_ipv4_addr_in_network.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv4.addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv4.addr_in_network(string addr, string cidr_net_addr) + * net.ipv4.ifnot_addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv4.ifnot_addr_in_network(string addr, string cidr_net_addr) + * + * Description: + * Checks if two IPv4 addresses belong to the same subnet. + * The prefix length is given either in the a separate argument or along with + * the second address in CIDR notation (address/prefix). + * This can be used to check whether an address belongs to a certain + * subnet, hence the name. + * + * Variables: + * (empty) - "true" if addresses belong to the same subnet, "false" if not + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_ifnot) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_addr; + NCDValRef arg_net_addr; + NCDValRef arg_net_prefix = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &arg_addr, &arg_net_addr) && + !NCDVal_ListRead(params->args, 3, &arg_addr, &arg_net_addr, &arg_net_prefix) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_addr) || !NCDVal_IsString(arg_net_addr) || + (!NCDVal_IsInvalid(arg_net_prefix) && !NCDVal_IsString(arg_net_prefix)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad address"); + goto fail0; + } + + // parse network + struct ipv4_ifaddr network; + if (NCDVal_IsInvalid(arg_net_prefix)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network)) { + ModuleLog(o->i, BLOG_ERROR, "bad network in CIDR notation"); + goto fail0; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network.addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad network address"); + goto fail0; + } + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(arg_net_prefix), NCDVal_StringLength(arg_net_prefix), &network.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "bad network prefix"); + goto fail0; + } + } + + // test + o->value = ipaddr_ipv4_addrs_in_network(addr, network.addr, network.prefix); + + if (is_ifnot && o->value) { + ModuleLog(o->i, BLOG_ERROR, "addresses belong to same subnet, not proceeding"); + } + + // signal up + if (!is_ifnot || !o->value) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_normal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 0); +} + +static void func_new_ifnot (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 1); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.addr_in_network", + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "ip_in_network", // compatibility name + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ipv4.ifnot_addr_in_network", + .func_new2 = func_new_ifnot, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_addr_in_network = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv4_arp_probe.c b/external/badvpn_dns/ncd/modules/net_ipv4_arp_probe.c new file mode 100644 index 00000000..6f8e45e7 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv4_arp_probe.c @@ -0,0 +1,212 @@ +/** + * @file net_ipv4_arp_probe.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * ARP probing module. + * + * Synopsis: + * net.ipv4.arp_probe(string ifname, string addr) + * + * Description: + * Monitors local presence of an IPv4 host on a network interface. + * On initialization, may take some time to determine whether + * the host is present or not, then goes to UP state. When it + * determines that presence has changed, toggles itself DOWN then + * UP to expose the new determination. + * + * Variables: + * exists - "true" if the host exists, "false" if not + */ + +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_UNKNOWN 1 +#define STATE_EXIST 2 +#define STATE_NOEXIST 3 + +struct instance { + NCDModuleInst *i; + BArpProbe arpprobe; + int state; +}; + +static void instance_free (struct instance *o, int is_error); + +static void arpprobe_handler (struct instance *o, int event) +{ + switch (event) { + case BARPPROBE_EVENT_EXIST: { + ASSERT(o->state == STATE_UNKNOWN || o->state == STATE_NOEXIST) + + ModuleLog(o->i, BLOG_INFO, "exist"); + + if (o->state == STATE_NOEXIST) { + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state exist + o->state = STATE_EXIST; + } break; + + case BARPPROBE_EVENT_NOEXIST: { + ASSERT(o->state == STATE_UNKNOWN || o->state == STATE_EXIST) + + ModuleLog(o->i, BLOG_INFO, "noexist"); + + if (o->state == STATE_EXIST) { + // signal down + NCDModuleInst_Backend_Down(o->i); + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state noexist + o->state = STATE_NOEXIST; + } break; + + case BARPPROBE_EVENT_ERROR: { + ModuleLog(o->i, BLOG_ERROR, "error"); + + // die + instance_free(o, 1); + return; + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_ifname; + NCDValRef arg_addr; + if (!NCDVal_ListRead(params->args, 2, &arg_ifname, &arg_addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(arg_ifname) || !NCDVal_IsString(arg_addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse address + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(arg_ifname, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // init arpprobe + int res = BArpProbe_Init(&o->arpprobe, ifname_nts.data, addr, i->params->iparams->reactor, o, (BArpProbe_handler)arpprobe_handler); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BArpProbe_Init failed"); + goto fail0; + } + + // set state unknown + o->state = STATE_UNKNOWN; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free arpprobe + BArpProbe_Free(&o->arpprobe); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_EXIST || o->state == STATE_NOEXIST) + + if (!strcmp(name, "exists")) { + *out = ncd_make_boolean(mem, o->state == STATE_EXIST, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.arp_probe", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_arp_probe = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv4_dhcp.c b/external/badvpn_dns/ncd/modules/net_ipv4_dhcp.c new file mode 100644 index 00000000..d9e8b74c --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv4_dhcp.c @@ -0,0 +1,351 @@ +/** + * @file net_ipv4_dhcp.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * DHCP client module. + * + * Synopsis: + * net.ipv4.dhcp(string ifname [, list opts]) + * + * Description: + * Runs a DHCP client on a network interface. When an address is obtained, + * transitions up (but does not assign anything). If the lease times out, + * transitions down. + * The interface must already be up. + * Supported options (in the opts argument): + * - "hostname", (string value): send this hostname to the DHCP server + * - "vendorclassid", (string value): send this vendor class identifier + * - "auto_clientid": send a client identifier generated from the MAC address + * + * Variables: + * string addr - assigned IP address ("A.B.C.D") + * string prefix - address prefix length ("N") + * string cidr_addr - address and prefix in CIDR notation ("A.B.C.D/N") + * string gateway - router address ("A.B.C.D"), or "none" if not provided + * list(string) dns_servers - DNS server addresses ("A.B.C.D" ...) + * string server_mac - MAC address of the DHCP server (6 two-digit caps hexadecimal values + * separated with colons, e.g."AB:CD:EF:01:02:03") + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + BDHCPClient dhcp; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void dhcp_handler (struct instance *o, int event) +{ + switch (event) { + case BDHCPCLIENT_EVENT_UP: { + ASSERT(!o->up) + o->up = 1; + NCDModuleInst_Backend_Up(o->i); + } break; + + case BDHCPCLIENT_EVENT_DOWN: { + ASSERT(o->up) + o->up = 0; + NCDModuleInst_Backend_Down(o->i); + } break; + + case BDHCPCLIENT_EVENT_ERROR: { + instance_free(o, 1); + return; + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ifname_arg; + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &ifname_arg) && !NCDVal_ListRead(params->args, 2, &ifname_arg, &opts_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || (!NCDVal_IsInvalid(opts_arg) && !NCDVal_IsList(opts_arg))) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValNullTermString hostname_nts = NCDValNullTermString_NewDummy(); + NCDValNullTermString vendorclassid_nts = NCDValNullTermString_NewDummy(); + + struct BDHCPClient_opts opts = {}; + + // read options + size_t count = NCDVal_IsInvalid(opts_arg) ? 0 : NCDVal_ListCount(opts_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef opt = NCDVal_ListGet(opts_arg, j); + + // read name + if (!NCDVal_IsString(opt)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option name type"); + goto fail1; + } + + if (NCDVal_StringEquals(opt, "hostname") || NCDVal_StringEquals(opt, "vendorclassid")) { + int is_hostname = NCDVal_StringEquals(opt, "hostname"); + + // read value + if (j == count) { + ModuleLog(o->i, BLOG_ERROR, "option value missing"); + goto fail1; + } + NCDValRef val = NCDVal_ListGet(opts_arg, j + 1); + if (!NCDVal_IsStringNoNulls(val)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option value type"); + goto fail1; + } + + // null terminate + NCDValNullTermString nts; + if (!NCDVal_StringNullTerminate(val, &nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + NCDValNullTermString *nts_ptr = (is_hostname ? &hostname_nts : &vendorclassid_nts); + NCDValNullTermString_Free(nts_ptr); + *nts_ptr = nts; + + if (is_hostname) { + opts.hostname = nts.data; + } else { + opts.vendorclassid = nts.data; + } + + j++; + } + else if (NCDVal_StringEquals(opt, "auto_clientid")) { + opts.auto_clientid = 1; + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown option name"); + goto fail1; + } + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail1; + } + + // init DHCP + int res = BDHCPClient_Init(&o->dhcp, ifname_nts.data, opts, o->i->params->iparams->reactor, o->i->params->iparams->random2, (BDHCPClient_handler)dhcp_handler, o); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "BDHCPClient_Init failed"); + goto fail1; + } + + // set not up + o->up = 0; + + // free options nts's + NCDValNullTermString_Free(&hostname_nts); + NCDValNullTermString_Free(&vendorclassid_nts); + return; + +fail1: + NCDValNullTermString_Free(&hostname_nts); + NCDValNullTermString_Free(&vendorclassid_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free DHCP + BDHCPClient_Free(&o->dhcp); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + + if (!strcmp(name, "addr")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + + char str[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr, str); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "prefix")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + uint32_t mask; + BDHCPClient_GetClientMask(&o->dhcp, &mask); + + struct ipv4_ifaddr ifaddr; + if (!ipaddr_ipv4_ifaddr_from_addr_mask(addr, mask, &ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "bad netmask"); + return 0; + } + + char str[10]; + sprintf(str, "%d", ifaddr.prefix); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "cidr_addr")) { + uint32_t addr; + BDHCPClient_GetClientIP(&o->dhcp, &addr); + uint32_t mask; + BDHCPClient_GetClientMask(&o->dhcp, &mask); + + struct ipv4_ifaddr ifaddr; + if (!ipaddr_ipv4_ifaddr_from_addr_mask(addr, mask, &ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "bad netmask"); + return 0; + } + + char str[IPADDR_PRINT_MAX]; + ipaddr_print_ifaddr(ifaddr, str); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "gateway")) { + char str[IPADDR_PRINT_MAX]; + + uint32_t addr; + if (!BDHCPClient_GetRouter(&o->dhcp, &addr)) { + strcpy(str, "none"); + } else { + ipaddr_print_addr(addr, str); + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "dns_servers")) { + uint32_t servers[BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS]; + int num_servers = BDHCPClient_GetDNS(&o->dhcp, servers, BDHCPCLIENT_MAX_DOMAIN_NAME_SERVERS); + + *out = NCDVal_NewList(mem, num_servers); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (int i = 0; i < num_servers; i++) { + char str[IPADDR_PRINT_MAX]; + ipaddr_print_addr(servers[i], str); + + NCDValRef server = NCDVal_NewString(mem, str); + if (NCDVal_IsInvalid(server)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, server)) { + goto fail; + } + } + + return 1; + } + + if (!strcmp(name, "server_mac")) { + uint8_t mac[6]; + BDHCPClient_GetServerMAC(&o->dhcp, mac); + + char str[18]; + sprintf(str, "%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8":%02"PRIX8, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.dhcp", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv4_route.c b/external/badvpn_dns/ncd/modules/net_ipv4_route.c new file mode 100644 index 00000000..0f343882 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv4_route.c @@ -0,0 +1,211 @@ +/** + * @file net_ipv4_route.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv4 route module. + * + * Synopsis: + * net.ipv4.route(string dest, string dest_prefix, string gateway, string metric, string ifname) + * net.ipv4.route(string cidr_dest, string gateway, string metric, string ifname) + * + * Description: + * Adds an IPv4 route to the system's routing table on initiailzation, and + * removes it on deinitialization. The second form takes the destination in + * CIDR notation (a.b.c.d/n). + * If 'gateway' is "none", the route will only be associated with an interface. + * If 'gateway' is "blackhole", the route will be a blackhole route (and 'ifname' is unused). + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define TYPE_NORMAL 1 +#define TYPE_IFONLY 2 +#define TYPE_BLACKHOLE 3 + +struct instance { + NCDModuleInst *i; + struct ipv4_ifaddr dest; + int type; + uint32_t gateway; + int metric; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef dest_arg; + NCDValRef dest_prefix_arg = NCDVal_NewInvalid(); + NCDValRef gateway_arg; + NCDValRef metric_arg; + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 4, &dest_arg, &gateway_arg, &metric_arg, &ifname_arg) && + !NCDVal_ListRead(params->args, 5, &dest_arg, &dest_prefix_arg, &gateway_arg, &metric_arg, &ifname_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(dest_arg) || !NCDVal_IsString(gateway_arg) || + !NCDVal_IsString(metric_arg) || !NCDVal_IsStringNoNulls(ifname_arg) || + (!NCDVal_IsInvalid(dest_prefix_arg) && !NCDVal_IsString(dest_prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read dest + if (NCDVal_IsInvalid(dest_prefix_arg)) { + if (!ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation dest"); + goto fail0; + } + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest addr"); + goto fail0; + } + if (!ipaddr_parse_ipv4_prefix_bin(NCDVal_StringData(dest_prefix_arg), NCDVal_StringLength(dest_prefix_arg), &o->dest.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest prefix"); + goto fail0; + } + } + + // read gateway and choose type + if (NCDVal_StringEquals(gateway_arg, "none")) { + o->type = TYPE_IFONLY; + } + else if (NCDVal_StringEquals(gateway_arg, "blackhole")) { + o->type = TYPE_BLACKHOLE; + } else { + if (!ipaddr_parse_ipv4_addr_bin(NCDVal_StringData(gateway_arg), NCDVal_StringLength(gateway_arg), &o->gateway)) { + ModuleLog(o->i, BLOG_ERROR, "wrong gateway"); + goto fail0; + } + o->type = TYPE_NORMAL; + } + + // read metric + uintmax_t metric; + if (!ncd_read_uintmax(metric_arg, &metric) || metric > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "bad metric"); + goto fail0; + } + o->metric = metric; + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // add route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_add_ipv4_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_add_ipv4_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_add_ipv4_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to add route"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_remove_ipv4_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_remove_ipv4_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_remove_ipv4_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove route"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv4.route", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv4_route = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv6_addr.c b/external/badvpn_dns/ncd/modules/net_ipv6_addr.c new file mode 100644 index 00000000..debee660 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv6_addr.c @@ -0,0 +1,148 @@ +/** + * @file net_ipv6_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 address module. + * + * Synopsis: + * net.ipv6.addr(string ifname, string addr, string prefix) + * net.ipv6.addr(string ifname, string cidr_addr) + * + * Description: + * Adds the given address to the given network interface on initialization, + * and removes it on deinitialization. The second form takes the address and + * prefix in CIDR notation (a.b.c.d/n). + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; + struct ipv6_ifaddr ifaddr; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + NCDValRef addr_arg; + NCDValRef prefix_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &ifname_arg, &addr_arg) && + !NCDVal_ListRead(params->args, 3, &ifname_arg, &addr_arg, &prefix_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg) || !NCDVal_IsString(addr_arg) || + (!NCDVal_IsInvalid(prefix_arg) && !NCDVal_IsString(prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + if (NCDVal_IsInvalid(prefix_arg)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation address"); + goto fail1; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &o->ifaddr.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong address"); + goto fail1; + } + + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &o->ifaddr.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong prefix"); + goto fail1; + } + } + + // add address + if (!NCDIfConfig_add_ipv6_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to add IP address"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove address + if (!NCDIfConfig_remove_ipv6_addr(o->ifname_nts.data, o->ifaddr)) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove IP address"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.addr", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_addr = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv6_addr_in_network.c b/external/badvpn_dns/ncd/modules/net_ipv6_addr_in_network.c new file mode 100644 index 00000000..1ae96774 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv6_addr_in_network.c @@ -0,0 +1,168 @@ +/** + * @file net_ipv6_addr_in_network.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv6.addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv6.addr_in_network(string addr, string cidr_net_addr) + * net.ipv6.ifnot_addr_in_network(string addr, string net_addr, string net_prefix) + * net.ipv6.ifnot_addr_in_network(string addr, string cidr_net_addr) + * + * Description: + * Checks if two IPv6 addresses belong to the same subnet. + * The prefix length is given either in the a separate argument or along with + * the second address in CIDR notation (address/prefix). + * This can be used to check whether an address belongs to a certain + * subnet, hence the name. + * + * Variables: + * (empty) - "true" if addresses belong to the same subnet, "false" if not + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int value; +}; + +static void func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_ifnot) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_addr; + NCDValRef arg_net_addr; + NCDValRef arg_net_prefix = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &arg_addr, &arg_net_addr) && + !NCDVal_ListRead(params->args, 3, &arg_addr, &arg_net_addr, &arg_net_prefix) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_addr) || !NCDVal_IsString(arg_net_addr) || + (!NCDVal_IsInvalid(arg_net_prefix) && !NCDVal_IsString(arg_net_prefix)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + struct ipv6_addr addr; + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(arg_addr), NCDVal_StringLength(arg_addr), &addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad address"); + goto fail0; + } + + // parse network + struct ipv6_ifaddr network; + if (NCDVal_IsInvalid(arg_net_prefix)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network)) { + ModuleLog(o->i, BLOG_ERROR, "bad network in CIDR notation"); + goto fail0; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(arg_net_addr), NCDVal_StringLength(arg_net_addr), &network.addr)) { + ModuleLog(o->i, BLOG_ERROR, "bad network address"); + goto fail0; + } + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(arg_net_prefix), NCDVal_StringLength(arg_net_prefix), &network.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "bad network prefix"); + goto fail0; + } + } + + // test + o->value = ipaddr6_ipv6_addrs_in_network(addr, network.addr, network.prefix); + + if (is_ifnot && o->value) { + ModuleLog(o->i, BLOG_ERROR, "addresses belong to same subnet, not proceeding"); + } + + // signal up + if (!is_ifnot || !o->value) { + NCDModuleInst_Backend_Up(o->i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_normal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 0); +} + +static void func_new_ifnot (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_common(vo, i, params, 1); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "")) { + *out = ncd_make_boolean(mem, o->value, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.addr_in_network", + .func_new2 = func_new_normal, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.ipv6.ifnot_addr_in_network", + .func_new2 = func_new_ifnot, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_addr_in_network = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv6_route.c b/external/badvpn_dns/ncd/modules/net_ipv6_route.c new file mode 100644 index 00000000..2f4ed0b0 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv6_route.c @@ -0,0 +1,213 @@ +/** + * @file net_ipv6_route.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * IPv6 route module. + * + * Synopsis: + * net.ipv6.route(string dest, string dest_prefix, string gateway, string metric, string ifname) + * net.ipv6.route(string cidr_dest, string gateway, string metric, string ifname) + * + * Description: + * Adds an IPv6 route to the system's routing table on initiailzation, and + * removes it on deinitialization. The second form takes the destination in + * CIDR notation (address/prefix). + * If 'gateway' is "none", the route will only be associated with an interface. + * If 'gateway' is "blackhole", the route will be a blackhole route (and 'ifname' is unused). + * NOTE: blackhole routes for IPv6 are not yet implemented in Linux; + * adding them via this interface will only work once they + * have been. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define TYPE_NORMAL 1 +#define TYPE_IFONLY 2 +#define TYPE_BLACKHOLE 3 + +struct instance { + NCDModuleInst *i; + struct ipv6_ifaddr dest; + int type; + struct ipv6_addr gateway; + int metric; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef dest_arg; + NCDValRef dest_prefix_arg = NCDVal_NewInvalid(); + NCDValRef gateway_arg; + NCDValRef metric_arg; + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 4, &dest_arg, &gateway_arg, &metric_arg, &ifname_arg) && + !NCDVal_ListRead(params->args, 5, &dest_arg, &dest_prefix_arg, &gateway_arg, &metric_arg, &ifname_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(dest_arg) || !NCDVal_IsString(gateway_arg) || + !NCDVal_IsString(metric_arg) || !NCDVal_IsStringNoNulls(ifname_arg) || + (!NCDVal_IsInvalid(dest_prefix_arg) && !NCDVal_IsString(dest_prefix_arg)) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read dest + if (NCDVal_IsInvalid(dest_prefix_arg)) { + if (!ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest)) { + ModuleLog(o->i, BLOG_ERROR, "wrong CIDR notation dest"); + goto fail0; + } + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(dest_arg), NCDVal_StringLength(dest_arg), &o->dest.addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest addr"); + goto fail0; + } + if (!ipaddr6_parse_ipv6_prefix_bin(NCDVal_StringData(dest_prefix_arg), NCDVal_StringLength(dest_prefix_arg), &o->dest.prefix)) { + ModuleLog(o->i, BLOG_ERROR, "wrong dest prefix"); + goto fail0; + } + } + + // read gateway and choose type + if (NCDVal_StringEquals(gateway_arg, "none")) { + o->type = TYPE_IFONLY; + } + else if (NCDVal_StringEquals(gateway_arg, "blackhole")) { + o->type = TYPE_BLACKHOLE; + } else { + if (!ipaddr6_parse_ipv6_addr_bin(NCDVal_StringData(gateway_arg), NCDVal_StringLength(gateway_arg), &o->gateway)) { + ModuleLog(o->i, BLOG_ERROR, "wrong gateway"); + goto fail0; + } + o->type = TYPE_NORMAL; + } + + // read metric + uintmax_t metric; + if (!ncd_read_uintmax(metric_arg, &metric) || metric > INT_MAX) { + ModuleLog(i, BLOG_ERROR, "bad metric"); + goto fail0; + } + o->metric = metric; + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // add route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_add_ipv6_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_add_ipv6_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_add_ipv6_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to add route"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // remove route + int res = 0; // to remove warning + switch (o->type) { + case TYPE_NORMAL: + res = NCDIfConfig_remove_ipv6_route(o->dest, &o->gateway, o->metric, o->ifname_nts.data); + break; + case TYPE_IFONLY: + res = NCDIfConfig_remove_ipv6_route(o->dest, NULL, o->metric, o->ifname_nts.data); + break; + case TYPE_BLACKHOLE: + res = NCDIfConfig_remove_ipv6_blackhole_route(o->dest, o->metric); + break; + default: ASSERT(0); + } + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to remove route"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.route", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_route = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_ipv6_wait_dynamic_addr.c b/external/badvpn_dns/ncd/modules/net_ipv6_wait_dynamic_addr.c new file mode 100644 index 00000000..f0404d5d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_ipv6_wait_dynamic_addr.c @@ -0,0 +1,201 @@ +/** + * @file net_ipv6_wait_dynamic_addr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * net.ipv6.wait_dynamic_addr(string ifname) + * + * Description: + * Waits for a dynamic IPv6 address to be obtained on the interface, + * and goes up when it is obtained. + * If the address is subsequently lost, goes back down and again waits + * for an address. + * + * Variables: + * string addr - dynamic address obtained on the interface + * string prefix - prefix length + * string cidr_addr - address and prefix (addr/prefix) + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDInterfaceMonitor monitor; + struct ipv6_ifaddr ifaddr; + int up; +}; + +static void instance_free (struct instance *o, int is_error); + +static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_event event) +{ + if (!o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED && (event.u.ipv6_addr.addr_flags & NCDIFMONITOR_ADDR_FLAG_DYNAMIC)) { + // rememeber address, set up + o->ifaddr = event.u.ipv6_addr.addr; + o->up = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); + } + else if (o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED && !memcmp(event.u.ipv6_addr.addr.addr.bytes, o->ifaddr.addr.bytes, 16) && event.u.ipv6_addr.addr.prefix == o->ifaddr.prefix) { + // set not up + o->up = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + } +} + +static void monitor_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "monitor error"); + + instance_free(o, 1); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + NCDValNullTermString ifname_nts; + if (!NCDVal_StringNullTerminate(ifname_arg, &ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // get interface index + int ifindex; + int res = badvpn_get_iface_info(ifname_nts.data, NULL, NULL, &ifindex); + NCDValNullTermString_Free(&ifname_nts); + if (!res) { + ModuleLog(o->i, BLOG_ERROR, "failed to get interface index"); + goto fail0; + } + + // init monitor + if (!NCDInterfaceMonitor_Init(&o->monitor, ifindex, NCDIFMONITOR_WATCH_IPV6_ADDR, i->params->iparams->reactor, o, (NCDInterfaceMonitor_handler)monitor_handler, (NCDInterfaceMonitor_handler_error)monitor_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed"); + goto fail0; + } + + // set not up + o->up = 0; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int is_error) +{ + // free monitor + NCDInterfaceMonitor_Free(&o->monitor); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->up) + + if (!strcmp(name, "addr")) { + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(o->ifaddr.addr, str); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "prefix")) { + char str[10]; + sprintf(str, "%d", o->ifaddr.prefix); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (!strcmp(name, "cidr_addr")) { + char str[IPADDR6_PRINT_MAX]; + ipaddr6_print_ifaddr(o->ifaddr, str); + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "net.ipv6.wait_dynamic_addr", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_ipv6_wait_dynamic_addr = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_up.c b/external/badvpn_dns/ncd/modules/net_up.c new file mode 100644 index 00000000..3aa04cec --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_up.c @@ -0,0 +1,119 @@ +/** + * @file net_up.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network interface up and down module. + * + * Synopsis: net.up(string ifname) + * Description: Sets a network interface up on initialization and down on + * deinitialization. + */ + +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValNullTermString ifname_nts; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef ifname_arg; + if (!NCDVal_ListRead(params->args, 1, &ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(ifname_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate ifname + if (!NCDVal_StringNullTerminate(ifname_arg, &o->ifname_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // set interface up + if (!NCDIfConfig_set_up(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set interface up"); + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + return; + +fail1: + NCDValNullTermString_Free(&o->ifname_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // set interface down + if (!NCDIfConfig_set_down(o->ifname_nts.data)) { + ModuleLog(o->i, BLOG_ERROR, "failed to set interface down"); + } + + // free ifname nts + NCDValNullTermString_Free(&o->ifname_nts); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "net.up", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_up = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/net_watch_interfaces.c b/external/badvpn_dns/ncd/modules/net_watch_interfaces.c new file mode 100644 index 00000000..7bd7b702 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/net_watch_interfaces.c @@ -0,0 +1,474 @@ +/** + * @file net_watch_interfaces.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network interface watcher. + * + * Synopsis: net.watch_interfaces() + * Description: reports network interface events. Transitions up when an event is detected, and + * goes down waiting for the next event when net.watch_interfaces::nextevent() is called. + * On startup, "added" events are reported for existing interfaces. + * Variables: + * string event_type - what happened with the interface: "added" or "removed". This may not be + * consistent across events. + * string devname - interface name + * string bus - bus location, for example "pci:0000:06:00.0", "usb:2-1.3:1.0", or "unknown" + * + * Synopsis: net.watch_interfaces::nextevent() + * Description: makes the watch_interfaces module transition down in order to report the next event. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define DEVPATH_REGEX "/net/[^/]+$" +#define DEVPATH_USB_REGEX "/usb[^/]*(/[^/]+)+/([^/]+)/net/[^/]+$" +#define DEVPATH_PCI_REGEX "/pci[^/]*/[^/]+/([^/]+)/net/[^/]+$" + +struct device { + char *ifname; + char *devpath; + uintmax_t ifindex; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + NCDUdevClient client; + LinkedList1 devices_list; + regex_t reg; + regex_t usb_reg; + regex_t pci_reg; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_ifname (struct instance *o, const char *ifname) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->ifname, ifname)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free ifname + free(device->ifname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *ifname, const char *bus, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set ifname + if (!BStringMap_Set(&map, "devname", ifname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set bus + if (!BStringMap_Set(&map, "bus", bus)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *ifname, const char *devpath, uintmax_t ifindex, const char *bus) +{ + ASSERT(!find_device_by_ifname(o, ifname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init ifname + if (!(device->ifname = strdup(ifname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // set ifindex + device->ifindex = ifindex; + + // init removed map + if (!make_event_map(o, 0, ifname, bus, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, ifname, bus, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->ifname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", ifname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void make_bus (struct instance *o, const char *devpath, const BStringMap *map, char *out_bus, size_t bus_avail) +{ + regmatch_t pmatch[3]; + + const char *type; + const char *id; + size_t id_len; + + if (!regexec(&o->usb_reg, devpath, 3, pmatch, 0)) { + type = "usb"; + id = devpath + pmatch[2].rm_so; + id_len = pmatch[2].rm_eo - pmatch[2].rm_so; + } + else if (!regexec(&o->pci_reg, devpath, 3, pmatch, 0)) { + type = "pci"; + id = devpath + pmatch[1].rm_so; + id_len = pmatch[1].rm_eo - pmatch[1].rm_so; + } else { + goto fail; + } + + size_t type_len = strlen(type); + bsize_t bus_len = bsize_add(bsize_fromsize(type_len), bsize_add(bsize_fromint(1), bsize_add(bsize_fromsize(id_len), bsize_fromint(1)))); + if (bus_len.is_overflow || bus_len.value > bus_avail) { + goto fail; + } + + memcpy(out_bus, type, type_len); + out_bus[type_len] = ':'; + memcpy(out_bus + type_len + 1, id, id_len); + out_bus[type_len + 1 + id_len] = '\0'; + return; + +fail: + snprintf(out_bus, bus_avail, "%s", "unknown"); +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + int match_res = regexec(&o->reg, devpath, 0, NULL, 0); + const char *interface = BStringMap_Get(cache_map, "INTERFACE"); + const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX"); + + uintmax_t ifindex; + if (!(!match_res && interface && ifindex_str && parse_unsigned_integer(ifindex_str, &ifindex))) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && (strcmp(ex_device->ifname, interface) || ex_device->ifindex != ifindex)) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_ifname_device = find_device_by_ifname(o, interface); + if (ex_ifname_device) { + remove_device(o, ex_ifname_device); + } + + char bus[128]; + make_bus(o, devpath, cache_map, bus, sizeof(bus)); + + add_device(o, interface, devpath, ifindex, bus); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + // compile regex's + if (regcomp(&o->reg, DEVPATH_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail1; + } + if (regcomp(&o->usb_reg, DEVPATH_USB_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail2; + } + if (regcomp(&o->pci_reg, DEVPATH_PCI_REGEX, REG_EXTENDED)) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed"); + goto fail3; + } + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail3: + regfree(&o->usb_reg); +fail2: + regfree(&o->reg); +fail1: + NCDUdevClient_Free(&o->client); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->devices_list)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + free_device(o, device, 1); + } + + // free regex's + regfree(&o->pci_reg); + regfree(&o->usb_reg); + regfree(&o->reg); + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "net.watch_interfaces", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "net.watch_interfaces::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_net_watch_interfaces = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/netmask.c b/external/badvpn_dns/ncd/modules/netmask.c new file mode 100644 index 00000000..53d93986 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/netmask.c @@ -0,0 +1,263 @@ +/** + * @file netmask.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * ipv4_prefix_to_mask(string prefix) + * + * Variables: + * string (empty) - prefix, converted to dotted decimal format without leading + * zeros + * + * Synopsis: + * ipv4_mask_to_prefix(string mask) + * + * Variables: + * string (empty) - mask, converted to prefix length + * + * Synopsis: + * ipv4_net_from_addr_and_prefix(string addr, string prefix) + * + * Variables: + * string (empty) - network part of the address, according to given prefix + * length, in dotted decimal format without leading zeros + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct addr_instance { + NCDModuleInst *i; + uint32_t addr; +}; + +struct prefix_instance { + NCDModuleInst *i; + int prefix; +}; + +static void addr_func_init_templ (void *vo, NCDModuleInst *i, uint32_t addr) +{ + struct addr_instance *o = vo; + o->i = i; + + // remember address + o->addr = addr; + + // signal up + NCDModuleInst_Backend_Up(i); +} + +static void prefix_to_mask_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef prefix_arg; + if (!NCDVal_ListRead(params->args, 1, &prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse prefix + int prefix; + if (!ipaddr_parse_ipv4_prefix_bin((char *)NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &prefix)) { + ModuleLog(i, BLOG_ERROR, "bad prefix"); + goto fail0; + } + + // make mask + uint32_t mask = ipaddr_ipv4_mask_from_prefix(prefix); + + addr_func_init_templ(vo, i, mask); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ipv4_net_from_addr_and_prefix_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef addr_arg; + NCDValRef prefix_arg; + if (!NCDVal_ListRead(params->args, 2, &addr_arg, &prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(addr_arg) || !NCDVal_IsString(prefix_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse addr + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(addr_arg), NCDVal_StringLength(addr_arg), &addr)) { + ModuleLog(i, BLOG_ERROR, "bad addr"); + goto fail0; + } + + // parse prefix + int prefix; + if (!ipaddr_parse_ipv4_prefix_bin((char *)NCDVal_StringData(prefix_arg), NCDVal_StringLength(prefix_arg), &prefix)) { + ModuleLog(i, BLOG_ERROR, "bad prefix"); + goto fail0; + } + + // make network + uint32_t network = (addr & ipaddr_ipv4_mask_from_prefix(prefix)); + + addr_func_init_templ(vo, i, network); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void addr_func_die (void *vo) +{ + struct addr_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct addr_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + char buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(o->addr, buf); + + *out = NCDVal_NewString(mem, buf); + return 1; + } + + return 0; +} + +static void mask_to_prefix_func_init (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct prefix_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef mask_arg; + if (!NCDVal_ListRead(params->args, 1, &mask_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(mask_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse mask + uint32_t mask; + if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(mask_arg), NCDVal_StringLength(mask_arg), &mask)) { + ModuleLog(i, BLOG_ERROR, "bad mask"); + goto fail0; + } + + // build prefix + if (!ipaddr_ipv4_prefix_from_mask(mask, &o->prefix)) { + ModuleLog(i, BLOG_ERROR, "bad mask"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void prefix_func_die (void *vo) +{ + struct prefix_instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int prefix_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct prefix_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + char buf[6]; + sprintf(buf, "%d", o->prefix); + + *out = NCDVal_NewString(mem, buf); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "ipv4_prefix_to_mask", + .func_new2 = prefix_to_mask_func_init, + .func_die = addr_func_die, + .func_getvar2 = addr_func_getvar2, + .alloc_size = sizeof(struct addr_instance) + }, { + .type = "ipv4_mask_to_prefix", + .func_new2 = mask_to_prefix_func_init, + .func_die = prefix_func_die, + .func_getvar2 = prefix_func_getvar2, + .alloc_size = sizeof(struct prefix_instance) + }, { + .type = "ipv4_net_from_addr_and_prefix", + .func_new2 = ipv4_net_from_addr_and_prefix_func_init, + .func_die = addr_func_die, + .func_getvar2 = addr_func_getvar2, + .alloc_size = sizeof(struct addr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_netmask = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/ondemand.c b/external/badvpn_dns/ncd/modules/ondemand.c new file mode 100644 index 00000000..15c2531f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/ondemand.c @@ -0,0 +1,372 @@ +/** + * @file ondemand.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * On-demand process manager. + * + * Synopsis: + * ondemand(string template_name, list args) + * + * Description: + * Manages an on-demand template process using a process template named + * template_name. + * On deinitialization, if the process is running, reqests its termination + * and waits for it to terminate. + * + * Synopsis: + * ondemand::demand() + * + * Description: + * Demands the availability of an on-demand template process. + * This statement is in UP state if and only if the template process of the + * corresponding ondemand object is completely up. + * + * Variables: + * Exposes variables and objects from the template process corresponding to + * the ondemand object. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct ondemand { + NCDModuleInst *i; + NCDValRef template_name; + NCDValRef args; + LinkedList1 demands_list; + int dying; + int have_process; + NCDModuleProcess process; + int process_terminating; + int process_up; +}; + +struct demand { + NCDModuleInst *i; + struct ondemand *od; + LinkedList1Node demands_list_node; +}; + +static int ondemand_start_process (struct ondemand *o); +static void ondemand_terminate_process (struct ondemand *o); +static void ondemand_process_handler (NCDModuleProcess *process, int event); +static void ondemand_free (struct ondemand *o); +static void demand_free (struct demand *o, int is_error); + +static int ondemand_start_process (struct ondemand *o) +{ + ASSERT(!o->dying) + ASSERT(!o->have_process) + + // start process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->template_name, o->args, ondemand_process_handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set have process + o->have_process = 1; + + // set process not terminating + o->process_terminating = 0; + + // set process not up + o->process_up = 0; + + return 1; + +fail0: + return 0; +} + +static void ondemand_terminate_process (struct ondemand *o) +{ + ASSERT(o->have_process) + ASSERT(!o->process_terminating) + + // request termination + NCDModuleProcess_Terminate(&o->process); + + // set process terminating + o->process_terminating = 1; + + if (o->process_up) { + // set process down + o->process_up = 0; + + // signal demands down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Down(demand->i); + } + } +} + +static void ondemand_process_handler (NCDModuleProcess *process, int event) +{ + struct ondemand *o = UPPER_OBJECT(process, struct ondemand, process); + ASSERT(o->have_process) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(!o->process_terminating) + ASSERT(!o->process_up) + + // set process up + o->process_up = 1; + + // signal demands up + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Up(demand->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(!o->process_terminating) + ASSERT(o->process_up) + + // continue process + NCDModuleProcess_Continue(&o->process); + + // set process down + o->process_up = 0; + + // signal demands down + for (LinkedList1Node *n = LinkedList1_GetFirst(&o->demands_list); n; n = LinkedList1Node_Next(n)) { + struct demand *demand = UPPER_OBJECT(n, struct demand, demands_list_node); + ASSERT(demand->od == o) + NCDModuleInst_Backend_Down(demand->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->process_terminating) + ASSERT(!o->process_up) + + // free process + NCDModuleProcess_Free(&o->process); + + // set have no process + o->have_process = 0; + + // if dying, die finally + if (o->dying) { + ondemand_free(o); + return; + } + + // if demands arrivied, restart process + if (!LinkedList1_IsEmpty(&o->demands_list)) { + if (!ondemand_start_process(o)) { + // error demands + while (!LinkedList1_IsEmpty(&o->demands_list)) { + struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node); + ASSERT(demand->od == o) + demand_free(demand, 1); + } + } + } + } break; + } +} + +static void ondemand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ondemand *o = vo; + o->i = i; + + // read arguments + NCDValRef arg_template_name; + NCDValRef arg_args; + if (!NCDVal_ListRead(params->args, 2, &arg_template_name, &arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(arg_template_name) || !NCDVal_IsList(arg_args)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->template_name = arg_template_name; + o->args = arg_args; + + // init demands list + LinkedList1_Init(&o->demands_list); + + // set not dying + o->dying = 0; + + // set have no process + o->have_process = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ondemand_free (struct ondemand *o) +{ + ASSERT(!o->have_process) + + // die demands + while (!LinkedList1_IsEmpty(&o->demands_list)) { + struct demand *demand = UPPER_OBJECT(LinkedList1_GetFirst(&o->demands_list), struct demand, demands_list_node); + ASSERT(demand->od == o) + demand_free(demand, 0); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void ondemand_func_die (void *vo) +{ + struct ondemand *o = vo; + ASSERT(!o->dying) + + // if not have process, die right away + if (!o->have_process) { + ondemand_free(o); + return; + } + + // set dying + o->dying = 1; + + // request process termination if not already + if (!o->process_terminating) { + ondemand_terminate_process(o); + } +} + +static void demand_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct demand *o = vo; + o->i = i; + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set ondemand + o->od = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // add to ondemand's demands list + LinkedList1_Append(&o->od->demands_list, &o->demands_list_node); + + // start process if needed + if (!o->od->have_process) { + ASSERT(!o->od->dying) + + if (!ondemand_start_process(o->od)) { + goto fail1; + } + } + + // if process is up, signal up + if (o->od->process_up) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail1: + LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void demand_free (struct demand *o, int is_error) +{ + // remove from ondemand's demands list + LinkedList1_Remove(&o->od->demands_list, &o->demands_list_node); + + // request process termination if no longer needed + if (o->od->have_process && !o->od->process_terminating && LinkedList1_IsEmpty(&o->od->demands_list)) { + ondemand_terminate_process(o->od); + } + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void demand_func_die (void *vo) +{ + struct demand *o = vo; + + demand_free(o, 0); +} + +static int demand_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct demand *o = vo; + ASSERT(o->od->have_process) + ASSERT(o->od->process_up) + + return NCDModuleProcess_GetObj(&o->od->process, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "ondemand", + .func_new2 = ondemand_func_new, + .func_die = ondemand_func_die, + .alloc_size = sizeof(struct ondemand) + }, { + .type = "ondemand::demand", + .func_new2 = demand_func_new, + .func_die = demand_func_die, + .func_getobj = demand_func_getobj, + .alloc_size = sizeof(struct demand) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_ondemand = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/parse.c b/external/badvpn_dns/ncd/modules/parse.c new file mode 100644 index 00000000..f4f40652 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/parse.c @@ -0,0 +1,392 @@ +/** + * @file parse.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * parse_number(string str) + * parse_value(string str) + * parse_ipv4_addr(string str) + * parse_ipv6_addr(string str) + * + * Variables: + * succeeded - "true" or "false", reflecting success of the parsing + * (empty) - normalized parsed value (only if succeeded) + * + * Synopsis: + * parse_ipv4_cidr_addr(string str) + * parse_ipv6_cidr_addr(string str) + * + * Variables: + * succeeded - "true" or "false", reflecting success of the parsing + * (empty) - normalized CIDR notation address (only if succeeded) + * addr - normalized address without prefix (only if succeeded) + * prefix - normalized prefix without address (only if succeeded) + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef value; + int succeeded; +}; + +struct ipv4_cidr_instance { + NCDModuleInst *i; + int succeeded; + struct ipv4_ifaddr ifaddr; +}; + +struct ipv6_cidr_instance { + NCDModuleInst *i; + int succeeded; + struct ipv6_ifaddr ifaddr; +}; + +enum {STRING_ADDR, STRING_PREFIX}; + +static const char *strings[] = { + "addr", "prefix", NULL +}; + +typedef int (*parse_func) (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out); + +static int parse_number (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + uintmax_t n; + if (!parse_unsigned_integer_bin(str, str_len, &n)) { + ModuleLog(i, BLOG_ERROR, "failed to parse number"); + return 0; + } + + *out = ncd_make_uintmax(mem, n); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static int parse_value (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + if (!NCDValParser_Parse(str, str_len, mem, out)) { + ModuleLog(i, BLOG_ERROR, "failed to parse value"); + return 0; + } + + return 1; +} + +static int parse_ipv4_addr (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + uint32_t addr; + if (!ipaddr_parse_ipv4_addr_bin(str, str_len, &addr)) { + ModuleLog(i, BLOG_ERROR, "failed to parse ipv4 addresss"); + return 0; + } + + char buf[IPADDR_PRINT_MAX]; + ipaddr_print_addr(addr, buf); + + *out = NCDVal_NewString(mem, buf); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static int parse_ipv6_addr (NCDModuleInst *i, const char *str, size_t str_len, NCDValMem *mem, NCDValRef *out) +{ + struct ipv6_addr addr; + if (!ipaddr6_parse_ipv6_addr_bin(str, str_len, &addr)) { + ModuleLog(i, BLOG_ERROR, "failed to parse ipv6 addresss"); + return 0; + } + + char buf[IPADDR6_PRINT_MAX]; + ipaddr6_print_addr(addr, buf); + + *out = NCDVal_NewString(mem, buf); + if (NCDVal_IsInvalid(*out)) { + return 0; + } + + return 1; +} + +static void new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, parse_func pfunc) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // parse + o->succeeded = pfunc(i, NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->mem, &o->value); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (o->succeeded && name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->value); + return 1; + } + + return 0; +} + +static void func_new_parse_number (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_number); +} + +static void func_new_parse_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_value); +} + +static void func_new_parse_ipv4_addr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_ipv4_addr); +} + +static void func_new_parse_ipv6_addr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, parse_ipv6_addr); +} + +static void ipv4_cidr_addr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ipv4_cidr_instance *o = vo; + o->i = i; + + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = ipaddr_parse_ipv4_ifaddr_bin(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->ifaddr); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int ipv4_cidr_addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct ipv4_cidr_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (!o->succeeded) { + return 0; + } + + char str[IPADDR_PRINT_MAX]; + + if (name == NCD_STRING_EMPTY) { + ipaddr_print_ifaddr(o->ifaddr, str); + } + else if (name == ModuleString(o->i, STRING_ADDR)) { + ipaddr_print_addr(o->ifaddr.addr, str); + } + else if (name == ModuleString(o->i, STRING_PREFIX)) { + sprintf(str, "%d", o->ifaddr.prefix); + } + else { + return 0; + } + + *out = NCDVal_NewString(mem, str); + return 1; +} + +static void ipv6_cidr_addr_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ipv6_cidr_instance *o = vo; + o->i = i; + + NCDValRef str_arg; + if (!NCDVal_ListRead(params->args, 1, &str_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + o->succeeded = ipaddr6_parse_ipv6_ifaddr_bin(NCDVal_StringData(str_arg), NCDVal_StringLength(str_arg), &o->ifaddr); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int ipv6_cidr_addr_func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct ipv6_cidr_instance *o = vo; + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + if (!o->succeeded) { + return 0; + } + + char str[IPADDR6_PRINT_MAX]; + + if (name == NCD_STRING_EMPTY) { + ipaddr6_print_ifaddr(o->ifaddr, str); + } + else if (name == ModuleString(o->i, STRING_ADDR)) { + ipaddr6_print_addr(o->ifaddr.addr, str); + } + else if (name == ModuleString(o->i, STRING_PREFIX)) { + sprintf(str, "%d", o->ifaddr.prefix); + } + else { + return 0; + } + + *out = NCDVal_NewString(mem, str); + return 1; +} + +static struct NCDModule modules[] = { + { + .type = "parse_number", + .func_new2 = func_new_parse_number, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_value", + .func_new2 = func_new_parse_value, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv4_addr", + .func_new2 = func_new_parse_ipv4_addr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv6_addr", + .func_new2 = func_new_parse_ipv6_addr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "parse_ipv4_cidr_addr", + .func_new2 = ipv4_cidr_addr_func_new, + .func_getvar2 = ipv4_cidr_addr_func_getvar2, + .alloc_size = sizeof(struct ipv4_cidr_instance) + }, { + .type = "parse_ipv6_cidr_addr", + .func_new2 = ipv6_cidr_addr_func_new, + .func_getvar2 = ipv6_cidr_addr_func_getvar2, + .alloc_size = sizeof(struct ipv6_cidr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_parse = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/print.c b/external/badvpn_dns/ncd/modules/print.c new file mode 100644 index 00000000..6fa0b56a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/print.c @@ -0,0 +1,207 @@ +/** + * @file print.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Modules for printing to standard output. + * + * Synopsis: + * print([string str ...]) + * Description: + * On initialization, prints strings to standard output. + * + * Synopsis: + * println([string str ...]) + * Description: + * On initialization, prints strings to standard output, and a newline. + * + * Synopsis: + * rprint([string str ...]) + * Description: + * On deinitialization, prints strings to standard output. + * + * Synopsis: + * rprintln([string str ...]) + * Description: + * On deinitialization, prints strings to standard output, and a newline. + */ + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct rprint_instance { + NCDModuleInst *i; + NCDValRef args; + int ln; +}; + +static int check_args (NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + size_t num_args = NCDVal_ListCount(params->args); + + for (size_t j = 0; j < num_args; j++) { + NCDValRef arg = NCDVal_ListGet(params->args, j); + if (!NCDVal_IsString(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + return 0; + } + } + + return 1; +} + +static void do_print (NCDModuleInst *i, NCDValRef args, int ln) +{ + size_t num_args = NCDVal_ListCount(args); + + for (size_t j = 0; j < num_args; j++) { + NCDValRef arg = NCDVal_ListGet(args, j); + ASSERT(NCDVal_IsString(arg)) + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + + B_CSTRING_LOOP_RANGE(arg_cstr, 0, arg_cstr.length, pos, chunk_data, chunk_length, { + size_t chunk_pos = 0; + while (chunk_pos < chunk_length) { + ssize_t res = fwrite(chunk_data + chunk_pos, 1, chunk_length - chunk_pos, stdout); + if (res <= 0) { + goto out; + } + chunk_pos += res; + } + }) + } + +out: + if (ln) { + printf("\n"); + } +} + +static void rprint_func_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int ln) +{ + struct rprint_instance *o = vo; + o->i = i; + o->args = params->args; + o->ln = ln; + + if (!check_args(i, params)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rprint_func_die (void *vo) +{ + struct rprint_instance *o = vo; + + do_print(o->i, o->args, o->ln); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void print_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!check_args(i, params)) { + goto fail0; + } + + do_print(i, params->args, 0); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void println_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!check_args(i, params)) { + goto fail0; + } + + do_print(i, params->args, 1); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void rprint_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + return rprint_func_new_common(vo, i, params, 0); +} + +static void rprintln_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + return rprint_func_new_common(vo, i, params, 1); +} + +static struct NCDModule modules[] = { + { + .type = "print", + .func_new2 = print_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "println", + .func_new2 = println_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "rprint", + .func_new2 = rprint_func_new, + .func_die = rprint_func_die, + .alloc_size = sizeof(struct rprint_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "rprintln", + .func_new2 = rprintln_func_new, + .func_die = rprint_func_die, + .alloc_size = sizeof(struct rprint_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_print = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/process_manager.c b/external/badvpn_dns/ncd/modules/process_manager.c new file mode 100644 index 00000000..390c8d50 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/process_manager.c @@ -0,0 +1,538 @@ +/** + * @file process_manager.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which allows starting and controlling template processes using an imperative + * interface. + * + * Synopsis: + * process_manager() + * + * Description: + * Represents a set of managed processes. Each process has a "name", which is a value + * that uniquely identifies it within its process manager. + * When deinitialization is requested, requests termination of all managed processes + * and waits for all of them to terminate before deinitializing. + * Managed processes can access objects as seen from the process_manager() statement + * via the special _caller object. + * + * Synopsis: + * process_manager::start(name, string template_name, list args) + * process_manager::start(string template_name, list args) + * + * Description: + * Creates a new process from the template named 'template_name', with arguments 'args', + * identified by 'name' within the process manager. If the two-argument form of start() is + * used, the process does not have a name, and cannot be imperatively stopped using + * stop(). + * If a process with this name already exists and is not being terminated, does nothing. + * If it exists and is being terminated, it will be restarted using the given parameters + * after it terminates. If the process does not exist, it is created immediately, and the + * immediate effects of the process being created happnen before the immediate effects of + * the start() statement going up. + * + * Synopsis: + * process_manager::stop(name) + * + * Description: + * Initiates termination of the process identified by 'name' within the process manager. + * If there is no such process, or the process is already being terminated, does nothing. + * If the process does exist and is not already being terminated, termination of the + * process is requested, and the immediate effects of the termination request happen + * before the immediate effects of the stop() statement going up. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define RETRY_TIME 10000 + +#define PROCESS_STATE_RUNNING 1 +#define PROCESS_STATE_STOPPING 2 +#define PROCESS_STATE_RESTARTING 3 +#define PROCESS_STATE_RETRYING 4 + +struct instance { + NCDModuleInst *i; + LinkedList1 processes_list; + int dying; +}; + +struct process { + struct instance *manager; + LinkedList1Node processes_list_node; + BSmallTimer retry_timer; // running if state=retrying + int state; + NCD_string_id_t template_name; + NCDValMem current_mem; + NCDValSafeRef current_name; + NCDValSafeRef current_args; + NCDValMem next_mem; // next_* if state=restarting + NCDValSafeRef next_name; + NCDValSafeRef next_args; + NCDModuleProcess module_process; // if state!=retrying +}; + +static struct process * find_process (struct instance *o, NCDValRef name); +static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args); +static void process_free (struct process *p); +static void process_try (struct process *p); +static void process_retry_timer_handler (BSmallTimer *retry_timer); +static void process_module_process_handler_event (NCDModuleProcess *module_process, int event); +static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object); +static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void process_stop (struct process *p); +static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args); +static void instance_free (struct instance *o); + +static struct process * find_process (struct instance *o, NCDValRef name) +{ + ASSERT(!NCDVal_IsInvalid(name)) + + LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list); + while (n) { + struct process *p = UPPER_OBJECT(n, struct process, processes_list_node); + ASSERT(p->manager == o) + if (!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) && NCDVal_Compare(NCDVal_FromSafe(&p->current_mem, p->current_name), name) == 0) { + return p; + } + n = LinkedList1Node_Next(n); + } + + return NULL; +} + +static int process_new (struct instance *o, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args) +{ + ASSERT(!o->dying) + ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(mem, name)) || !find_process(o, NCDVal_FromSafe(mem, name))) + ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name))) + ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args))) + + // allocate structure + struct process *p = BAlloc(sizeof(*p)); + if (!p) { + ModuleLog(o->i, BLOG_ERROR, "BAlloc failed"); + goto fail0; + } + + // set manager + p->manager = o; + + // insert to processes list + LinkedList1_Append(&o->processes_list, &p->processes_list_node); + + // init retry timer + BSmallTimer_Init(&p->retry_timer, process_retry_timer_handler); + + // init template name + p->template_name = ncd_get_string_id(NCDVal_FromSafe(mem, template_name), o->i->params->iparams->string_index); + if (p->template_name < 0) { + ModuleLog(o->i, BLOG_ERROR, "ncd_get_string_id failed"); + goto fail1; + } + + // init current mem as a copy of mem + if (!NCDValMem_InitCopy(&p->current_mem, mem)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed"); + goto fail1; + } + + // remember name and args + p->current_name = name; + p->current_args = args; + + // try starting it + process_try(p); + return 1; + +fail1: + LinkedList1_Remove(&o->processes_list, &p->processes_list_node); + BFree(p); +fail0: + return 0; +} + +static void process_free (struct process *p) +{ + struct instance *o = p->manager; + + // free current mem + NCDValMem_Free(&p->current_mem); + + // free timer + BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &p->retry_timer); + + // remove from processes list + LinkedList1_Remove(&o->processes_list, &p->processes_list_node); + + // free structure + BFree(p); +} + +static void process_try (struct process *p) +{ + struct instance *o = p->manager; + ASSERT(!o->dying) + ASSERT(!BSmallTimer_IsRunning(&p->retry_timer)) + + // init module process + if (!NCDModuleProcess_InitId(&p->module_process, o->i, p->template_name, NCDVal_FromSafe(&p->current_mem, p->current_args), process_module_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&p->module_process, process_module_process_func_getspecialobj); + + // set state + p->state = PROCESS_STATE_RUNNING; + return; + +fail: + // set timer + BReactor_SetSmallTimer(o->i->params->iparams->reactor, &p->retry_timer, BTIMER_SET_RELATIVE, RETRY_TIME); + + // set state + p->state = PROCESS_STATE_RETRYING; +} + +static void process_retry_timer_handler (BSmallTimer *retry_timer) +{ + struct process *p = UPPER_OBJECT(retry_timer, struct process, retry_timer); + struct instance *o = p->manager; + B_USE(o) + ASSERT(p->state == PROCESS_STATE_RETRYING) + ASSERT(!o->dying) + + // retry + process_try(p); +} + +void process_module_process_handler_event (NCDModuleProcess *module_process, int event) +{ + struct process *p = UPPER_OBJECT(module_process, struct process, module_process); + struct instance *o = p->manager; + ASSERT(p->state != PROCESS_STATE_RETRYING) + ASSERT(p->state != PROCESS_STATE_RESTARTING || !o->dying) + ASSERT(!BSmallTimer_IsRunning(&p->retry_timer)) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(p->state == PROCESS_STATE_RUNNING) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(p->state == PROCESS_STATE_RUNNING) + + // allow process to continue + NCDModuleProcess_Continue(&p->module_process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(p->state == PROCESS_STATE_RESTARTING || p->state == PROCESS_STATE_STOPPING) + + // free module process + NCDModuleProcess_Free(&p->module_process); + + if (p->state == PROCESS_STATE_RESTARTING) { + // free current mem + NCDValMem_Free(&p->current_mem); + + // move next mem/values over current mem/values + p->current_mem = p->next_mem; + p->current_name = p->next_name; + p->current_args = p->next_args; + + // try starting it again + process_try(p); + return; + } + + // free process + process_free(p); + + // if manager is dying and there are no more processes, let it die + if (o->dying && LinkedList1_IsEmpty(&o->processes_list)) { + instance_free(o); + } + } break; + } +} + +static int process_module_process_func_getspecialobj (NCDModuleProcess *module_process, NCD_string_id_t name, NCDObject *out_object) +{ + struct process *p = UPPER_OBJECT(module_process, struct process, module_process); + ASSERT(p->state != PROCESS_STATE_RETRYING) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, p, NCDObject_no_getvar, process_module_process_caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int process_module_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct process *p = NCDObject_DataPtr(obj); + struct instance *o = p->manager; + ASSERT(p->state != PROCESS_STATE_RETRYING) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void process_stop (struct process *p) +{ + switch (p->state) { + case PROCESS_STATE_RETRYING: { + // free process + process_free(p); + } break; + + case PROCESS_STATE_RUNNING: { + // request process to terminate + NCDModuleProcess_Terminate(&p->module_process); + + // set state + p->state = PROCESS_STATE_STOPPING; + } break; + + case PROCESS_STATE_RESTARTING: { + // free next mem + NCDValMem_Free(&p->next_mem); + + // set state + p->state = PROCESS_STATE_STOPPING; + } break; + + case PROCESS_STATE_STOPPING: { + // nothing to do + } break; + + default: ASSERT(0); + } +} + +static int process_restart (struct process *p, NCDValMem *mem, NCDValSafeRef name, NCDValSafeRef template_name, NCDValSafeRef args) +{ + struct instance *o = p->manager; + ASSERT(!o->dying) + ASSERT(p->state == PROCESS_STATE_STOPPING) + ASSERT(!NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_IsInvalid(NCDVal_FromSafe(mem, name))) + ASSERT(NCDVal_IsInvalid(NCDVal_FromSafe(&p->current_mem, p->current_name)) || NCDVal_Compare(NCDVal_FromSafe(mem, name), NCDVal_FromSafe(&p->current_mem, p->current_name)) == 0) + ASSERT(NCDVal_IsString(NCDVal_FromSafe(mem, template_name))) + ASSERT(NCDVal_IsList(NCDVal_FromSafe(mem, args))) + + // copy mem to next mem + if (!NCDValMem_InitCopy(&p->next_mem, mem)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValMem_InitCopy failed"); + goto fail0; + } + + // remember name and args to next + p->next_name = name; + p->next_args = args; + + // set state + p->state = PROCESS_STATE_RESTARTING; + return 1; + +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init processes list + LinkedList1_Init(&o->processes_list); + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->processes_list)) + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // request all processes to die + LinkedList1Node *n = LinkedList1_GetFirst(&o->processes_list); + while (n) { + LinkedList1Node *next = LinkedList1Node_Next(n); + struct process *p = UPPER_OBJECT(n, struct process, processes_list_node); + process_stop(p); + n = next; + } + + // if there are no processes, die immediately + if (LinkedList1_IsEmpty(&o->processes_list)) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; +} + +static void start_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg = NCDVal_NewInvalid(); + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg) && + !NCDVal_ListRead(params->args, 3, &name_arg, &template_name_arg, &args_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up. + // Do it before creating the process so that the process starts initializing before our own process continues. + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (!mo->dying) { + struct process *p = (NCDVal_IsInvalid(name_arg) ? NULL : find_process(mo, name_arg)); + if (!p || p->state == PROCESS_STATE_STOPPING) { + if (p) { + if (!process_restart(p, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) { + ModuleLog(i, BLOG_ERROR, "failed to restart process"); + goto fail0; + } + } else { + if (!process_new(mo, args_arg.mem, NCDVal_ToSafe(name_arg), NCDVal_ToSafe(template_name_arg), NCDVal_ToSafe(args_arg))) { + ModuleLog(i, BLOG_ERROR, "failed to create process"); + goto fail0; + } + } + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void stop_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef name_arg; + if (!NCDVal_ListRead(params->args, 1, &name_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // signal up. + // Do it before stopping the process so that the process starts terminating before our own process continues. + NCDModuleInst_Backend_Up(i); + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + if (!mo->dying) { + struct process *p = find_process(mo, name_arg); + if (p && p->state != PROCESS_STATE_STOPPING) { + process_stop(p); + } + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "process_manager", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "process_manager::start", + .func_new2 = start_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "process_manager::stop", + .func_new2 = stop_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_process_manager = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/reboot.c b/external/badvpn_dns/ncd/modules/reboot.c new file mode 100644 index 00000000..3522431f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/reboot.c @@ -0,0 +1,103 @@ +/** + * @file reboot.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * hard_reboot() + * hard_poweroff() + */ + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void func_new_hard_reboot (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // reboot + if (reboot(RB_AUTOBOOT) < 0) { + ModuleLog(i, BLOG_ERROR, "reboot(RB_AUTOBOOT) failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_hard_poweroff (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // power off + if (reboot(RB_POWER_OFF) < 0) { + ModuleLog(i, BLOG_ERROR, "reboot(RB_POWER_OFF) failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "hard_reboot", + .func_new2 = func_new_hard_reboot + }, { + .type = "hard_poweroff", + .func_new2 = func_new_hard_poweroff + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_reboot = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/ref.c b/external/badvpn_dns/ncd/modules/ref.c new file mode 100644 index 00000000..a0e9cf84 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/ref.c @@ -0,0 +1,215 @@ +/** + * @file ref.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * References module. + * + * Synopsis: + * refhere() + * Variables: + * Exposes variables and objects as seen from this refhere() statement. + * + * Synopsis: + * ref refhere::ref() + * ref ref::ref() + * Variables: + * Exposes variables and objects as seen from the corresponding refhere() + * statement. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct refhere_instance { + NCDModuleInst *i; + LinkedList0 refs_list; +}; + +struct ref_instance { + NCDModuleInst *i; + struct refhere_instance *rh; + LinkedList0Node refs_list_node; +}; + +static void ref_instance_free (struct ref_instance *o); + +static void refhere_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct refhere_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init refs list + LinkedList0_Init(&o->refs_list); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void refhere_func_die (void *vo) +{ + struct refhere_instance *o = vo; + + // die refs + while (!LinkedList0_IsEmpty(&o->refs_list)) { + struct ref_instance *ref = UPPER_OBJECT(LinkedList0_GetFirst(&o->refs_list), struct ref_instance, refs_list_node); + ASSERT(ref->rh == o) + ref_instance_free(ref); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int refhere_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct refhere_instance *o = vo; + + // We don't redirect methods, and there will never be an object + // with empty name. Fail here so we don't report non-errors. + if (objname == NCD_STRING_EMPTY) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->i, objname, out_object); +} + +static void ref_func_new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, struct refhere_instance *rh) +{ + struct ref_instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // set refhere + o->rh = rh; + + // add to refhere's refs list + LinkedList0_Prepend(&o->rh->refs_list, &o->refs_list_node); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void ref_func_new_from_refhere (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct refhere_instance *rh = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + return ref_func_new_templ(vo, i, params, rh); +} + +static void ref_func_new_from_ref (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct ref_instance *ref = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + return ref_func_new_templ(vo, i, params, ref->rh); +} + +static void ref_instance_free (struct ref_instance *o) +{ + // remove from refhere's reft list + LinkedList0_Remove(&o->rh->refs_list, &o->refs_list_node); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void ref_func_die (void *vo) +{ + struct ref_instance *o = vo; + + ref_instance_free(o); +} + +static int ref_func_getobj (void *vo, NCD_string_id_t objname, NCDObject *out_object) +{ + struct ref_instance *o = vo; + + // We don't redirect methods, and there will never be an object + // with empty name. Fail here so we don't report non-errors. + if (objname == NCD_STRING_EMPTY) { + return 0; + } + + return NCDModuleInst_Backend_GetObj(o->rh->i, objname, out_object); +} + +static struct NCDModule modules[] = { + { + .type = "refhere", + .func_new2 = refhere_func_new, + .func_die = refhere_func_die, + .func_getobj = refhere_func_getobj, + .alloc_size = sizeof(struct refhere_instance) + }, { + .type = "refhere::ref", + .base_type = "ref", + .func_new2 = ref_func_new_from_refhere, + .func_die = ref_func_die, + .func_getobj = ref_func_getobj, + .alloc_size = sizeof(struct ref_instance) + }, { + .type = "ref::ref", + .base_type = "ref", + .func_new2 = ref_func_new_from_ref, + .func_die = ref_func_die, + .func_getobj = ref_func_getobj, + .alloc_size = sizeof(struct ref_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_ref = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/regex_match.c b/external/badvpn_dns/ncd/modules/regex_match.c new file mode 100644 index 00000000..d541b887 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/regex_match.c @@ -0,0 +1,369 @@ +/** + * @file regex_match.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Regular expression matching module. + * + * Synopsis: + * regex_match(string input, string regex) + * + * Variables: + * succeeded - "true" or "false", indicating whether input matched regex + * matchN - for N=0,1,2,..., the matching data for the N-th subexpression + * (match0 = whole match) + * + * Description: + * Matches 'input' with the POSIX extended regular expression 'regex'. + * 'regex' must be a string without null bytes, but 'input' can contain null bytes. + * However, it's difficult, if not impossible, to actually match nulls with the regular + * expression. + * The input and regex strings are interpreted according to the POSIX regex functions + * (regcomp(), regexec()); in particular, the current locale setting affects the + * interpretation. + * + * Synopsis: + * regex_replace(string input, list(string) regex, list(string) replace) + * + * Variables: + * string (empty) - transformed input + * + * Description: + * Replaces matching parts of a string. Replacement is performed by repetedly matching + * the remaining part of the string with all regular expressions. On each step, out of + * all regular expressions that match the remainder of the string, the one whose match + * starts at the least position wins, and the matching part is replaced with the + * replacement string corresponding to this regular expression. The process continues + * from the end of the just-replaced portion until no more regular expressions match. + * If multiple regular expressions match at the least position, the one that appears + * first in the 'regex' argument wins. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define MAX_MATCHES 64 + +struct instance { + NCDModuleInst *i; + const char *input; + size_t input_len; + int succeeded; + int num_matches; + regmatch_t matches[MAX_MATCHES]; +}; + +struct replace_instance { + NCDModuleInst *i; + char *output; + size_t output_len; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef input_arg; + NCDValRef regex_arg; + if (!NCDVal_ListRead(params->args, 2, &input_arg, ®ex_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(input_arg) || !NCDVal_IsStringNoNulls(regex_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->input = NCDVal_StringData(input_arg); + o->input_len = NCDVal_StringLength(input_arg); + + // make sure we don't overflow regoff_t + if (o->input_len > INT_MAX) { + ModuleLog(o->i, BLOG_ERROR, "input string too long"); + goto fail0; + } + + // null terminate regex + NCDValNullTermString regex_nts; + if (!NCDVal_StringNullTerminate(regex_arg, ®ex_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // compile regex + regex_t preg; + int ret = regcomp(&preg, regex_nts.data, REG_EXTENDED); + NCDValNullTermString_Free(®ex_nts); + if (ret != 0) { + ModuleLog(o->i, BLOG_ERROR, "regcomp failed (error=%d)", ret); + goto fail0; + } + + // execute match + o->matches[0].rm_so = 0; + o->matches[0].rm_eo = o->input_len; + o->succeeded = (regexec(&preg, o->input, MAX_MATCHES, o->matches, REG_STARTEND) == 0); + + // free regex + regfree(&preg); + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (!strcmp(name, "succeeded")) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + size_t pos; + uintmax_t n; + if ((pos = string_begins_with(name, "match")) && parse_unsigned_integer(name + pos, &n)) { + if (o->succeeded && n < MAX_MATCHES && o->matches[n].rm_so >= 0) { + regmatch_t *m = &o->matches[n]; + + ASSERT(m->rm_so <= o->input_len) + ASSERT(m->rm_eo >= m->rm_so) + ASSERT(m->rm_eo <= o->input_len) + + size_t len = m->rm_eo - m->rm_so; + + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->input + m->rm_so, len); + return 1; + } + } + + return 0; +} + +static void replace_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct replace_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef input_arg; + NCDValRef regex_arg; + NCDValRef replace_arg; + if (!NCDVal_ListRead(params->args, 3, &input_arg, ®ex_arg, &replace_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsString(input_arg) || !NCDVal_IsList(regex_arg) || !NCDVal_IsList(replace_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // check number of regex/replace + if (NCDVal_ListCount(regex_arg) != NCDVal_ListCount(replace_arg)) { + ModuleLog(i, BLOG_ERROR, "number of regex's is not the same as number of replacements"); + goto fail1; + } + size_t num_regex = NCDVal_ListCount(regex_arg); + + // allocate array for compiled regex's + regex_t *regs = BAllocArray(num_regex, sizeof(regs[0])); + if (!regs) { + ModuleLog(i, BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + size_t num_done_regex = 0; + + // compile regex's, check arguments + while (num_done_regex < num_regex) { + NCDValRef regex = NCDVal_ListGet(regex_arg, num_done_regex); + NCDValRef replace = NCDVal_ListGet(replace_arg, num_done_regex); + + if (!NCDVal_IsStringNoNulls(regex) || !NCDVal_IsString(replace)) { + ModuleLog(i, BLOG_ERROR, "wrong regex/replace type for pair %zu", num_done_regex); + goto fail2; + } + + // null terminate regex + NCDValNullTermString regex_nts; + if (!NCDVal_StringNullTerminate(regex, ®ex_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail2; + } + + int res = regcomp(®s[num_done_regex], regex_nts.data, REG_EXTENDED); + NCDValNullTermString_Free(®ex_nts); + if (res != 0) { + ModuleLog(i, BLOG_ERROR, "regcomp failed for pair %zu (error=%d)", num_done_regex, res); + goto fail2; + } + + num_done_regex++; + } + + // init output string + ExpString out; + if (!ExpString_Init(&out)) { + ModuleLog(i, BLOG_ERROR, "ExpString_Init failed"); + goto fail2; + } + + // input state + const char *in = NCDVal_StringData(input_arg); + size_t in_pos = 0; + size_t in_len = NCDVal_StringLength(input_arg); + + // process input + while (in_pos < in_len) { + // find first match + int have_match = 0; + size_t match_regex = 0; // to remove warning + regmatch_t match = {0, 0}; // to remove warning + for (size_t j = 0; j < num_regex; j++) { + regmatch_t this_match; + this_match.rm_so = 0; + this_match.rm_eo = in_len - in_pos; + if (regexec(®s[j], in + in_pos, 1, &this_match, REG_STARTEND) == 0 && (!have_match || this_match.rm_so < match.rm_so)) { + have_match = 1; + match_regex = j; + match = this_match; + } + } + + // if no match, append remaining data and finish + if (!have_match) { + if (!ExpString_AppendBinary(&out, (const uint8_t *)in + in_pos, in_len - in_pos)) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + break; + } + + // append data before match + if (!ExpString_AppendBinary(&out, (const uint8_t *)in + in_pos, match.rm_so)) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + + // append replacement data + NCDValRef replace = NCDVal_ListGet(replace_arg, match_regex); + if (!ExpString_AppendBinary(&out, (const uint8_t *)NCDVal_StringData(replace), NCDVal_StringLength(replace))) { + ModuleLog(i, BLOG_ERROR, "ExpString_AppendBinary failed"); + goto fail3; + } + + in_pos += match.rm_eo; + } + + // set output + o->output = ExpString_Get(&out); + o->output_len = ExpString_Length(&out); + + // free compiled regex's + while (num_done_regex-- > 0) { + regfree(®s[num_done_regex]); + } + + // free array + BFree(regs); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail3: + ExpString_Free(&out); +fail2: + while (num_done_regex-- > 0) { + regfree(®s[num_done_regex]); + } + BFree(regs); +fail1: + NCDModuleInst_Backend_DeadError(i); +} + +static void replace_func_die (void *vo) +{ + struct replace_instance *o = vo; + + // free output + BFree(o->output); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int replace_func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct replace_instance *o = vo; + + if (!strcmp(name, "")) { + *out = NCDVal_NewStringBin(mem, (uint8_t *)o->output, o->output_len); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "regex_match", + .func_new2 = func_new, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "regex_replace", + .func_new2 = replace_func_new, + .func_die = replace_func_die, + .func_getvar = replace_func_getvar, + .alloc_size = sizeof(struct replace_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_regex_match = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/run.c b/external/badvpn_dns/ncd/modules/run.c new file mode 100644 index 00000000..147914cc --- /dev/null +++ b/external/badvpn_dns/ncd/modules/run.c @@ -0,0 +1,187 @@ +/** + * @file run.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module for running arbitrary programs. + * NOTE: There is no locking - the program may run in parallel with other + * NCD processes and their programs. + * + * Synopsis: run(list do_cmd, list undo_cmd) + * Arguments: + * list do_cmd - Command run on startup. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). An empty list is interpreted as no operation. + * list undo_cmd - Command run on shutdown, like do_cmd. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +static void template_free_func (void *vo, int is_error); + +struct instance { + NCDModuleInst *i; + BEventLock lock; + command_template_instance cti; +}; + +static int build_cmdline (NCDModuleInst *i, NCDValRef args, int remove, char **exec, CmdLine *cl) +{ + // read arguments + NCDValRef do_cmd_arg; + NCDValRef undo_cmd_arg; + if (!NCDVal_ListRead(args, 2, &do_cmd_arg, &undo_cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(do_cmd_arg) || !NCDVal_IsList(undo_cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + NCDValRef list = (remove ? undo_cmd_arg : do_cmd_arg); + size_t count = NCDVal_ListCount(list); + + // check if there is no command + if (count == 0) { + *exec = NULL; + return 1; + } + + // read exec + NCDValRef exec_arg = NCDVal_ListGet(list, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "ncd_strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(list, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, arg_cstr, 0, arg_cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init dummy event lock + BEventLock_Init(&o->lock, BReactor_PendingGroup(i->params->iparams->reactor)); + + command_template_new(&o->cti, i, params, build_cmdline, template_free_func, o, BLOG_CURRENT_CHANNEL, &o->lock); + return; +} + +void template_free_func (void *vo, int is_error) +{ + struct instance *o = vo; + + // free dummy event lock + BEventLock_Free(&o->lock); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + command_template_die(&o->cti); +} + +static struct NCDModule modules[] = { + { + .type = "run", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_run= { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/runonce.c b/external/badvpn_dns/ncd/modules/runonce.c new file mode 100644 index 00000000..bd7cea40 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/runonce.c @@ -0,0 +1,331 @@ +/** + * @file runonce.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Imperative program execution module. On initialization, starts the process. + * Goes to UP state when the process terminates. When requested to die, waits for + * the process to terminate if it's running, optionally sending SIGTERM. + * + * Synopsis: runonce(list(string) cmd, [list opts]) + * Arguments: + * cmd - Command to run on startup. The first element is the full path + * to the executable, other elements are command line arguments (excluding + * the zeroth argument). + * opts - Map of options: + * "term_on_deinit":"true" - If we get a deinit request while the process is + * running, send it SIGTERM. + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + * Variables: + * string exit_status - if the program exited normally, the non-negative exit code, otherwise -1 + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_RUNNING 1 +#define STATE_RUNNING_DIE 2 +#define STATE_FINISHED 3 + +struct instance { + NCDModuleInst *i; + int term_on_deinit; + int state; + BProcess process; + int exit_status; +}; + +static void instance_free (struct instance *o); + +static int build_cmdline (NCDModuleInst *i, NCDValRef cmd_arg, char **exec, CmdLine *cl) +{ + ASSERT(!NCDVal_IsInvalid(cmd_arg)) + + if (!NCDVal_IsList(cmd_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + size_t count = NCDVal_ListCount(cmd_arg); + + // read exec + if (count == 0) { + ModuleLog(i, BLOG_ERROR, "missing executable name"); + goto fail0; + } + NCDValRef exec_arg = NCDVal_ListGet(cmd_arg, 0); + if (!NCDVal_IsStringNoNulls(exec_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + if (!(*exec = ncd_strdup(exec_arg))) { + ModuleLog(i, BLOG_ERROR, "strdup failed"); + goto fail0; + } + + // start cmdline + if (!CmdLine_Init(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Init failed"); + goto fail1; + } + + // add header + if (!CmdLine_Append(cl, *exec)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Append failed"); + goto fail2; + } + + // add additional arguments + for (size_t j = 1; j < count; j++) { + NCDValRef arg = NCDVal_ListGet(cmd_arg, j); + + if (!NCDVal_IsStringNoNulls(arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail2; + } + + b_cstring arg_cstr = NCDVal_StringCstring(arg); + if (!CmdLine_AppendCstring(cl, arg_cstr, 0, arg_cstr.length)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_AppendCstring failed"); + goto fail2; + } + } + + // finish + if (!CmdLine_Finish(cl)) { + ModuleLog(i, BLOG_ERROR, "CmdLine_Finish failed"); + goto fail2; + } + + return 1; + +fail2: + CmdLine_Free(cl); +fail1: + free(*exec); +fail0: + return 0; +} + +static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status) +{ + ASSERT(o->state == STATE_RUNNING || o->state == STATE_RUNNING_DIE) + + // free process + BProcess_Free(&o->process); + + // if we were requested to die, die now + if (o->state == STATE_RUNNING_DIE) { + instance_free(o); + return; + } + + // remember exit status + o->exit_status = (normally ? normally_exit_status : -1); + + // set state + o->state = STATE_FINISHED; + + // set up + NCDModuleInst_Backend_Up(o->i); +} + +static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val) +{ + struct instance *o = user; + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) { + o->term_on_deinit = ncd_read_boolean(val); + return 1; + } + + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // init arguments + o->term_on_deinit = 0; + + // read arguments + NCDValRef cmd_arg; + NCDValRef opts_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &cmd_arg) && !NCDVal_ListRead(params->args, 2, &cmd_arg, &opts_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + NCDBProcessOpts opts; + + // deprecated options format + if (!NCDVal_IsInvalid(opts_arg) && NCDVal_IsList(opts_arg)) { + int keep_stdout = 0; + int keep_stderr = 0; + int do_setsid = 0; + + // read options + size_t count = NCDVal_IsInvalid(opts_arg) ? 0 : NCDVal_ListCount(opts_arg); + for (size_t j = 0; j < count; j++) { + NCDValRef opt = NCDVal_ListGet(opts_arg, j); + + // read name + if (!NCDVal_IsString(opt)) { + ModuleLog(o->i, BLOG_ERROR, "wrong option name type"); + goto fail0; + } + + if (NCDVal_StringEquals(opt, "term_on_deinit")) { + o->term_on_deinit = 1; + } + else if (NCDVal_StringEquals(opt, "keep_stdout")) { + keep_stdout = 1; + } + else if (NCDVal_StringEquals(opt, "keep_stderr")) { + keep_stderr = 1; + } + else if (NCDVal_StringEquals(opt, "do_setsid")) { + do_setsid = 1; + } + else { + ModuleLog(o->i, BLOG_ERROR, "unknown option name"); + goto fail0; + } + } + + NCDBProcessOpts_InitOld(&opts, keep_stdout, keep_stderr, do_setsid); + } else { + if (!NCDBProcessOpts_Init(&opts, opts_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL)) { + goto fail0; + } + } + + // build cmdline + char *exec; + CmdLine cl; + if (!build_cmdline(o->i, cmd_arg, &exec, &cl)) { + NCDBProcessOpts_Free(&opts); + goto fail0; + } + + // start process + struct BProcess_params p_params = NCDBProcessOpts_GetParams(&opts); + if (!BProcess_Init2(&o->process, o->i->params->iparams->manager, (BProcess_handler)process_handler, o, exec, CmdLine_Get(&cl), p_params)) { + ModuleLog(i, BLOG_ERROR, "BProcess_Init failed"); + CmdLine_Free(&cl); + free(exec); + NCDBProcessOpts_Free(&opts); + goto fail0; + } + + CmdLine_Free(&cl); + free(exec); + NCDBProcessOpts_Free(&opts); + + // set state + o->state = STATE_RUNNING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_RUNNING_DIE) + + if (o->state == STATE_FINISHED) { + instance_free(o); + return; + } + + // send SIGTERM if requested + if (o->term_on_deinit) { + BProcess_Terminate(&o->process); + } + + o->state = STATE_RUNNING_DIE; +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_FINISHED) + + if (!strcmp(name, "exit_status")) { + char str[30]; + snprintf(str, sizeof(str), "%d", o->exit_status); + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "runonce", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_runonce = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/sleep.c b/external/badvpn_dns/ncd/modules/sleep.c new file mode 100644 index 00000000..a139aa9a --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sleep.c @@ -0,0 +1,178 @@ +/** + * @file sleep.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sleep(string ms_start [, string ms_stop]) + * + * Description: + * On init, sleeps 'ms_start' milliseconds then goes up, or goes up immediately + * if 'ms_start' is an empty string. + * On deinit, sleeps 'ms_stop' milliseconds then dies, or dies immediately if + * 'ms_stop' is an empty string or is not provided. If a deinit is requested while + * the init sleep is still in progress, the init sleep is aborted and the deinit + * sleep is started immediately (if any). + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + btime_t ms_stop; + BTimer timer; + int dying; +}; + +static void instance_free (struct instance *o); + +static void timer_handler (void *vo) +{ + struct instance *o = vo; + + if (!o->dying) { + // signal up + NCDModuleInst_Backend_Up(o->i); + } else { + // die + instance_free(o); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef ms_start_arg; + NCDValRef ms_stop_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &ms_start_arg) && + !NCDVal_ListRead(params->args, 2, &ms_start_arg, &ms_stop_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(ms_start_arg) || (!NCDVal_IsInvalid(ms_stop_arg) && !NCDVal_IsString(ms_stop_arg))) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t ms; + btime_t ms_start; + + if (NCDVal_StringEqualsId(ms_start_arg, NCD_STRING_EMPTY, i->params->iparams->string_index)) { + ms_start = -1; + } else { + if (!ncd_read_uintmax(ms_start_arg, &ms) || ms > INT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong start time"); + goto fail0; + } + ms_start = ms; + } + + if (NCDVal_IsInvalid(ms_stop_arg) || NCDVal_StringEqualsId(ms_stop_arg, NCD_STRING_EMPTY, i->params->iparams->string_index)) { + o->ms_stop = -1; + } else { + if (!ncd_read_uintmax(ms_stop_arg, &ms) || ms > INT64_MAX) { + ModuleLog(o->i, BLOG_ERROR, "wrong stop time"); + goto fail0; + } + o->ms_stop = ms; + } + + // init timer + BTimer_Init(&o->timer, 0, timer_handler, o); + + // set not dying + o->dying = 0; + + if (ms_start < 0) { + // go up + NCDModuleInst_Backend_Up(i); + } else { + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, ms_start); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + if (o->ms_stop < 0) { + // die immediately + instance_free(o); + return; + } + + // set dying + o->dying = 1; + + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->ms_stop); +} + +static struct NCDModule modules[] = { + { + .type = "sleep", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sleep = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/socket.c b/external/badvpn_dns/ncd/modules/socket.c new file mode 100644 index 00000000..59ca8d74 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/socket.c @@ -0,0 +1,1057 @@ +/** + * @file socket.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.socket sys.connect(string addr [, map options]) + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Variables: + * string is_error - "true" if there was an error with the connection, + * "false" if not + * + * Description: + * Attempts to establish a connection to a server. The address should be + * in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * When the connection attempt is finished, the sys.connect() statement goes + * up, and the 'is_error' variable should be used to check for connection + * failure. If there was no error, the read(), write() and close() methods + * can be used to work with the connection. + * If an error occurs after the connection has been established, the + * sys.connect() statement will automatically trigger backtracking, and the + * 'is_error' variable will be changed to "true". This means that all + * errors with the connection can be handled at the place of sys.connect(), + * and no special care is normally needed to handle error in read() and + * write(). + * WARNING: when you're not trying to either send or receive data, the + * connection may be unable to detect any events with the connection. + * You should never be neither sending nor receiving for an indefinite time. + * + * Synopsis: + * sys.socket::read() + * + * Variables: + * string (empty) - some data received from the socket, or empty on EOF + * string not_eof - "true" if EOF was not encountered, "false" if it was + * + * Description: + * Receives data from the connection. If EOF was encountered (remote host + * has closed the connection), this returns no data. Otherwise it returns + * at least one byte. + * WARNING: after you receive EOF from a sys.listen() type socket, is is + * your responsibility to call close() eventually, else the cline process + * may remain alive indefinitely. + * WARNING: this may return an arbitrarily small chunk of data. There is + * no significance to the size of the chunks. Correct code will behave + * the same no matter how the incoming data stream is split up. + * WARNING: if a read() is terminated while it is still in progress, i.e. + * has not gone up yet, then the connection is automatically closed, as + * if close() was called. + * + * Synopsis: + * sys.socket::write(string data) + * + * Description: + * Sends data to the connection. + * WARNING: this may block if the operating system's internal send buffer + * is full. Be careful not to enter a deadlock where both ends of the + * connection are trying to send data to the other, but neither is trying + * to receive any data. + * WARNING: if a write() is terminated while it is still in progress, i.e. + * has not gone up yet, then the connection is automatically closed, as + * if close() was called. + * + * Synopsis: + * sys.socket::close() + * + * Description: + * Closes the connection. After this, any further read(), write() or close() + * will trigger an error with the interpreter. For client sockets created + * via sys.listen(), this will immediately trigger termination of the client + * process. + * + * Synopsis: + * sys.listen(string address, string client_template, list args [, map options]) + * + * Options: + * "read_size" - the maximum number of bytes that can be read by a single + * read() call. Must be greater than zero. Greater values may improve + * performance, but will increase memory usage. Default: 8192. + * + * Variables: + * string is_error - "true" if listening failed to inittialize, "false" if + * not + * + * Special objects and variables in client_template: + * sys.socket _socket - the socket object for the client + * string _socket.client_addr - the address of the client. The form is + * like the second part of the sys.connect() address format, e.g. + * {"ipv4", "1.2.3.4", "4000"}. + * + * Description: + * Starts listening on the specified address. The 'is_error' variable + * reflects the success of listening initiation. If listening succeeds, + * then for every client that connects, a process is automatically created + * from the template specified by 'client_template', and the 'args' list + * is used as template arguments. Inside such processes, a special object + * '_socket' is available, which represents the connection, and supports + * the same methods as sys.connect(), i.e. read(), write() and close(). + * When an error occurs with the connection, the socket is automatically + * closed, triggering process termination. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define CONNECTION_TYPE_CONNECT 1 +#define CONNECTION_TYPE_LISTEN 2 + +#define CONNECTION_STATE_CONNECTING 1 +#define CONNECTION_STATE_ESTABLISHED 2 +#define CONNECTION_STATE_ERROR 3 +#define CONNECTION_STATE_ABORTED 4 + +#define DEFAULT_READ_BUF_SIZE 8192 + +struct connection { + union { + struct { + NCDModuleInst *i; + BConnector connector; + size_t read_buf_size; + } connect; + struct { + struct listen_instance *listen_inst; + LinkedList0Node clients_list_node; + BAddr addr; + NCDModuleProcess process; + } listen; + }; + + unsigned int type:2; + unsigned int state:3; + unsigned int recv_closed:1; + BConnection connection; + NCDBufStore store; + struct read_instance *read_inst; + struct write_instance *write_inst; +}; + +struct read_instance { + NCDModuleInst *i; + struct connection *con_inst; + NCDBuf *buf; + size_t read_size; +}; + +struct write_instance { + NCDModuleInst *i; + struct connection *con_inst; + b_cstring cstr; + size_t pos; +}; + +struct listen_instance { + NCDModuleInst *i; + unsigned int have_error:1; + unsigned int dying:1; + size_t read_buf_size; + NCDValRef client_template; + NCDValRef client_template_args; + BListener listener; + LinkedList0 clients_list; +}; + +enum {STRING_SOCKET, STRING_SYS_SOCKET, STRING_CLIENT_ADDR}; + +static const char *strings[] = { + "_socket", "sys.socket", "client_addr", NULL +}; + +static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size); +static void connection_log (struct connection *o, int level, const char *fmt, ...); +static void connection_free_connection (struct connection *o); +static void connection_error (struct connection *o); +static void connection_abort (struct connection *o); +static void connection_connector_handler (void *user, int is_error); +static void connection_connection_handler (void *user, int event); +static void connection_send_handler_done (void *user, int data_len); +static void connection_recv_handler_done (void *user, int data_len); +static void connection_process_handler (struct NCDModuleProcess_s *process, int event); +static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object); +static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static void listen_listener_handler (void *user); + +static int parse_options (NCDModuleInst *i, NCDValRef options, size_t *out_read_size) +{ + ASSERT(out_read_size) + + *out_read_size = DEFAULT_READ_BUF_SIZE; + + if (!NCDVal_IsInvalid(options)) { + if (!NCDVal_IsMap(options)) { + ModuleLog(i, BLOG_ERROR, "options argument is not a map"); + return 0; + } + + int num_recognized = 0; + NCDValRef value; + + if (!NCDVal_IsInvalid(value = NCDVal_MapGetValue(options, "read_size"))) { + uintmax_t read_size; + if (!NCDVal_IsString(value) || !ncd_read_uintmax(value, &read_size) || read_size > SIZE_MAX || read_size == 0) { + ModuleLog(i, BLOG_ERROR, "wrong read_size"); + return 0; + } + num_recognized++; + *out_read_size = read_size; + } + + if (NCDVal_MapCount(options) > num_recognized) { + ModuleLog(i, BLOG_ERROR, "unrecognized options present"); + return 0; + } + } + + return 1; +} + +static void connection_log (struct connection *o, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + switch (o->type) { + case CONNECTION_TYPE_CONNECT: { + NCDModuleInst_Backend_LogVarArg(o->connect.i, BLOG_CURRENT_CHANNEL, level, fmt, vl); + } break; + + case CONNECTION_TYPE_LISTEN: { + if (BLog_WouldLog(BLOG_CURRENT_CHANNEL, level)) { + BLog_Begin(); + o->listen.listen_inst->i->params->logfunc(o->listen.listen_inst->i); + char addr_str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&o->listen.addr, addr_str); + BLog_Append("client %s: ", addr_str); + BLog_AppendVarArg(fmt, vl); + BLog_Finish(BLOG_CURRENT_CHANNEL, level); + } + } break; + + default: ASSERT(0); + } + + va_end(vl); +} + +static void connection_free_connection (struct connection *o) +{ + // disconnect read instance + if (o->read_inst) { + ASSERT(o->read_inst->con_inst == o) + o->read_inst->con_inst = NULL; + } + + // disconnect write instance + if (o->write_inst) { + ASSERT(o->write_inst->con_inst == o) + o->write_inst->con_inst = NULL; + } + + // free connection interfaces + BConnection_RecvAsync_Free(&o->connection); + BConnection_SendAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // free store + NCDBufStore_Free(&o->store); +} + +static void connection_error (struct connection *o) +{ + ASSERT(o->state == CONNECTION_STATE_CONNECTING || + o->state == CONNECTION_STATE_ESTABLISHED) + + // for listen clients, we don't report errors directly, + // we just terminate the client process + if (o->type == CONNECTION_TYPE_LISTEN) { + ASSERT(o->state != CONNECTION_STATE_CONNECTING) + connection_abort(o); + return; + } + + // free connector + if (o->state == CONNECTION_STATE_CONNECTING) { + BConnector_Free(&o->connect.connector); + } + + // free connection resources + if (o->state == CONNECTION_STATE_ESTABLISHED) { + connection_free_connection(o); + } + + // trigger reporting of failure + if (o->state == CONNECTION_STATE_ESTABLISHED) { + NCDModuleInst_Backend_Down(o->connect.i); + } + NCDModuleInst_Backend_Up(o->connect.i); + + // set state + o->state = CONNECTION_STATE_ERROR; +} + +static void connection_abort (struct connection *o) +{ + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + + // free connection resources + connection_free_connection(o); + + // if this is a listen connection, terminate client process + if (o->type == CONNECTION_TYPE_LISTEN) { + NCDModuleProcess_Terminate(&o->listen.process); + } + + // set state + o->state = CONNECTION_STATE_ABORTED; +} + +static void connection_connector_handler (void *user, int is_error) +{ + struct connection *o = user; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + ASSERT(o->state == CONNECTION_STATE_CONNECTING) + + // check error + if (is_error) { + connection_log(o, BLOG_ERROR, "connection failed"); + goto fail; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_connector(&o->connect.connector), o->connect.i->params->iparams->reactor, o, connection_connection_handler)) { + connection_log(o, BLOG_ERROR, "BConnection_Init failed"); + goto fail; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->connection); + BConnection_RecvAsync_Init(&o->connection); + + // setup send/recv done callbacks + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), connection_send_handler_done, o); + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), connection_recv_handler_done, o); + + // init store + NCDBufStore_Init(&o->store, o->connect.read_buf_size); + + // set not reading, not writing, recv not closed + o->read_inst = NULL; + o->write_inst = NULL; + o->recv_closed = 0; + + // free connector + BConnector_Free(&o->connect.connector); + + // set state + o->state = CONNECTION_STATE_ESTABLISHED; + + // go up + NCDModuleInst_Backend_Up(o->connect.i); + return; + +fail: + connection_error(o); +} + +static void connection_connection_handler (void *user, int event) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(event == BCONNECTION_EVENT_RECVCLOSED || event == BCONNECTION_EVENT_ERROR) + ASSERT(event != BCONNECTION_EVENT_RECVCLOSED || !o->recv_closed) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + // if we have read operation, make it finish with eof + if (o->read_inst) { + ASSERT(o->read_inst->con_inst == o) + o->read_inst->con_inst = NULL; + o->read_inst->read_size = 0; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; + } + + // set recv closed + o->recv_closed = 1; + return; + } + + connection_log(o, BLOG_ERROR, "connection error"); + + // handle error + connection_error(o); +} + +static void connection_send_handler_done (void *user, int data_len) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->write_inst) + ASSERT(o->write_inst->con_inst == o) + ASSERT(o->write_inst->pos < o->write_inst->cstr.length) + ASSERT(data_len > 0) + ASSERT(data_len <= o->write_inst->cstr.length - o->write_inst->pos) + + struct write_instance *wr = o->write_inst; + + // update send state + wr->pos += data_len; + + // if there's more to send, send again + if (wr->pos < wr->cstr.length) { + size_t chunk_len; + const char *chunk_data = b_cstring_get(wr->cstr, wr->pos, wr->cstr.length - wr->pos, &chunk_len); + size_t to_send = (chunk_len > INT_MAX ? INT_MAX : chunk_len); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)chunk_data, to_send); + return; + } + + // finish write operation + wr->con_inst = NULL; + NCDModuleInst_Backend_Up(wr->i); + o->write_inst = NULL; +} + +static void connection_recv_handler_done (void *user, int data_len) +{ + struct connection *o = user; + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->read_inst) + ASSERT(o->read_inst->con_inst == o) + ASSERT(!o->recv_closed) + ASSERT(data_len > 0) + ASSERT(data_len <= NCDBufStore_BufSize(&o->store)) + + struct read_instance *re = o->read_inst; + + // finish read operation + re->con_inst = NULL; + re->read_size = data_len; + NCDModuleInst_Backend_Up(re->i); + o->read_inst = NULL; +} + +static void connection_process_handler (struct NCDModuleProcess_s *process, int event) +{ + struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == CONNECTION_STATE_ESTABLISHED) + NCDModuleProcess_Continue(&o->listen.process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == CONNECTION_STATE_ABORTED) + + struct listen_instance *li = o->listen.listen_inst; + ASSERT(!li->have_error) + + // remove from clients list + LinkedList0_Remove(&li->clients_list, &o->listen.clients_list_node); + + // free process + NCDModuleProcess_Free(&o->listen.process); + + // free connection structure + free(o); + + // if listener is dying and this was the last process, have it die + if (li->dying && LinkedList0_IsEmpty(&li->clients_list)) { + NCDModuleInst_Backend_Dead(li->i); + } + } break; + + default: ASSERT(0); + } +} + +static int connection_process_func_getspecialobj (struct NCDModuleProcess_s *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct connection *o = UPPER_OBJECT(process, struct connection, listen.process); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + if (name == ModuleString(o->listen.listen_inst->i, STRING_SOCKET)) { + *out_object = NCDObject_Build(ModuleString(o->listen.listen_inst->i, STRING_SYS_SOCKET), o, connection_process_socket_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int connection_process_socket_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value) +{ + struct connection *o = NCDObject_DataPtr(obj); + ASSERT(o->type == CONNECTION_TYPE_LISTEN) + + if (name == ModuleString(o->listen.listen_inst->i, STRING_CLIENT_ADDR)) { + *out_value = ncd_make_baddr(o->listen.addr, mem); + if (NCDVal_IsInvalid(*out_value)) { + connection_log(o, BLOG_ERROR, "ncd_make_baddr failed"); + } + return 1; + } + + return 0; +} + +static void listen_listener_handler (void *user) +{ + struct listen_instance *o = user; + ASSERT(!o->have_error) + ASSERT(!o->dying) + + // allocate connection structure + struct connection *con = malloc(sizeof(*con)); + if (!con) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set connection type and listen instance + con->type = CONNECTION_TYPE_LISTEN; + con->listen.listen_inst = o; + + // init connection + if (!BConnection_Init(&con->connection, BConnection_source_listener(&o->listener, &con->listen.addr), o->i->params->iparams->reactor, con, connection_connection_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init connection interfaces + BConnection_SendAsync_Init(&con->connection); + BConnection_RecvAsync_Init(&con->connection); + + // setup send/recv done callbacks + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&con->connection), connection_send_handler_done, con); + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&con->connection), connection_recv_handler_done, con); + + // init process + if (!NCDModuleProcess_InitValue(&con->listen.process, o->i, o->client_template, o->client_template_args, connection_process_handler)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_InitValue failed"); + goto fail2; + } + + // set special objects callback + NCDModuleProcess_SetSpecialFuncs(&con->listen.process, connection_process_func_getspecialobj); + + // insert to clients list + LinkedList0_Prepend(&o->clients_list, &con->listen.clients_list_node); + + // init store + NCDBufStore_Init(&con->store, o->read_buf_size); + + // set not reading, not writing, recv not closed + con->read_inst = NULL; + con->write_inst = NULL; + con->recv_closed = 0; + + // set state + con->state = CONNECTION_STATE_ESTABLISHED; + return; + +fail2: + BConnection_RecvAsync_Free(&con->connection); + BConnection_SendAsync_Free(&con->connection); + BConnection_Free(&con->connection); +fail1: + free(con); +fail0: + return; +} + +static void connect_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct connection *o = vo; + o->type = CONNECTION_TYPE_CONNECT; + o->connect.i = i; + + // pass connection pointer to methods so the same methods can work for + // listen type connections + NCDModuleInst_Backend_PassMemToMethods(i); + + // read arguments + NCDValRef address_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &address_arg) && + !NCDVal_ListRead(params->args, 2, &address_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // parse options + if (!parse_options(i, options_arg, &o->connect.read_buf_size)) { + goto fail0; + } + + // read address + struct BConnection_addr address; + if (!ncd_read_bconnection_addr(address_arg, &address)) { + ModuleLog(i, BLOG_ERROR, "wrong address"); + goto error; + } + + // init connector + if (!BConnector_InitGeneric(&o->connect.connector, address, i->params->iparams->reactor, o, connection_connector_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnector_InitGeneric failed"); + goto error; + } + + // set state + o->state = CONNECTION_STATE_CONNECTING; + return; + +error: + // go up in error state + o->state = CONNECTION_STATE_ERROR; + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void connect_func_die (void *vo) +{ + struct connection *o = vo; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + + // free connector + if (o->state == CONNECTION_STATE_CONNECTING) { + BConnector_Free(&o->connect.connector); + } + + // free connection resources + if (o->state == CONNECTION_STATE_ESTABLISHED) { + connection_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->connect.i); +} + +static int connect_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct connection *o = vo; + ASSERT(o->type == CONNECTION_TYPE_CONNECT) + ASSERT(o->state != CONNECTION_STATE_CONNECTING) + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == CONNECTION_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->connect.i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // check if there's already a read in progress + if (con_inst->read_inst) { + ModuleLog(i, BLOG_ERROR, "read is already in progress"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&con_inst->store); + if (!o->buf) { + ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // if eof was reached, go up immediately + if (con_inst->recv_closed) { + o->con_inst = NULL; + o->read_size = 0; + NCDModuleInst_Backend_Up(i); + return; + } + + // set connection + o->con_inst = con_inst; + + // register read operation in connection + con_inst->read_inst = o; + + // receive + size_t buf_size = NCDBufStore_BufSize(&con_inst->store); + int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size); + StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&con_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // if we're receiving, abort connection + if (o->con_inst) { + ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->con_inst->read_inst == o) + connection_abort(o->con_inst); + } + + // release buffer + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + ASSERT(!o->con_inst) + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf)); + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + *out = ncd_make_boolean(mem, (o->read_size != 0), o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // check if there's already a write in progress + if (con_inst->write_inst) { + ModuleLog(i, BLOG_ERROR, "write is already in progress"); + goto fail0; + } + + // set send state + o->cstr = NCDVal_StringCstring(data_arg); + o->pos = 0; + + // if there's nothing to send, go up immediately + if (o->cstr.length == 0) { + o->con_inst = NULL; + NCDModuleInst_Backend_Up(i); + return; + } + + // set connection + o->con_inst = con_inst; + + // register write operation in connection + con_inst->write_inst = o; + + // send + size_t chunk_len; + const char *chunk_data = b_cstring_get(o->cstr, o->pos, o->cstr.length - o->pos, &chunk_len); + size_t to_send = (chunk_len > INT_MAX ? INT_MAX : chunk_len); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&con_inst->connection), (uint8_t *)chunk_data, to_send); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_func_die (void *vo) +{ + struct write_instance *o = vo; + + // if we're sending, abort connection + if (o->con_inst) { + ASSERT(o->con_inst->state == CONNECTION_STATE_ESTABLISHED) + ASSERT(o->con_inst->write_inst == o) + connection_abort(o->con_inst); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get connection + struct connection *con_inst = params->method_user; + + // check connection state + if (con_inst->state != CONNECTION_STATE_ESTABLISHED) { + ModuleLog(i, BLOG_ERROR, "connection is not established"); + goto fail0; + } + + // abort + connection_abort(con_inst); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void listen_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct listen_instance *o = vo; + o->i = i; + + // read arguments + NCDValRef address_arg; + NCDValRef client_template_arg; + NCDValRef args_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 3, &address_arg, &client_template_arg, &args_arg) && + !NCDVal_ListRead(params->args, 4, &address_arg, &client_template_arg, &args_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(client_template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // parse options + if (!parse_options(i, options_arg, &o->read_buf_size)) { + goto fail0; + } + + // remember client template and arguments + o->client_template = client_template_arg; + o->client_template_args = args_arg; + + // set no error, not dying + o->have_error = 0; + o->dying = 0; + + // read address + struct BConnection_addr address; + if (!ncd_read_bconnection_addr(address_arg, &address)) { + ModuleLog(i, BLOG_ERROR, "wrong address"); + goto error; + } + + // init listener + if (!BListener_InitGeneric(&o->listener, address, i->params->iparams->reactor, o, listen_listener_handler)) { + ModuleLog(i, BLOG_ERROR, "BListener_InitGeneric failed"); + goto error; + } + + // init clients list + LinkedList0_Init(&o->clients_list); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +error: + // go up with error + o->have_error = 1; + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void listen_func_die (void *vo) +{ + struct listen_instance *o = vo; + ASSERT(!o->dying) + + // free listener + if (!o->have_error) { + BListener_Free(&o->listener); + } + + // if we have no clients, die right away + if (o->have_error || LinkedList0_IsEmpty(&o->clients_list)) { + NCDModuleInst_Backend_Dead(o->i); + return; + } + + // set dying + o->dying = 1; + + // abort all clients and wait for them + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clients_list); ln; ln = LinkedList0Node_Next(ln)) { + struct connection *con = UPPER_OBJECT(ln, struct connection, listen.clients_list_node); + ASSERT(con->type == CONNECTION_TYPE_LISTEN) + ASSERT(con->listen.listen_inst == o) + ASSERT(con->state == CONNECTION_STATE_ESTABLISHED || con->state == CONNECTION_STATE_ABORTED) + + if (con->state != CONNECTION_STATE_ABORTED) { + connection_abort(con); + } + } +} + +static int listen_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct listen_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + *out = ncd_make_boolean(mem, o->have_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "sys.connect", + .base_type = "sys.socket", + .func_new2 = connect_func_new, + .func_die = connect_func_die, + .func_getvar2 = connect_func_getvar, + .alloc_size = sizeof(struct connection), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::write", + .func_new2 = write_func_new, + .func_die = write_func_die, + .alloc_size = sizeof(struct write_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.socket::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.listen", + .func_new2 = listen_func_new, + .func_die = listen_func_die, + .func_getvar2 = listen_func_getvar, + .alloc_size = sizeof(struct listen_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_socket = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/spawn.c b/external/badvpn_dns/ncd/modules/spawn.c new file mode 100644 index 00000000..4f686708 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/spawn.c @@ -0,0 +1,410 @@ +/** + * @file spawn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Module which starts a process from a process template on initialization, and + * stops it on deinitialization. + * + * Synopsis: + * spawn(string template_name, list args) + * + * Description: + * On initialization, creates a new process from the template named + * 'template_name', with arguments 'args'. On deinitialization, initiates termination + * of the process and waits for it to terminate. The process can access objects + * as seen from 'spawn' via the _caller special object. + * + * Synopsis: + * spawn::join() + * + * Description: + * A join() on a spawn() is like a depend() on a provide() which is located at the + * end of the spawned process. + * + * Variables: + * Exposes objects from the spawned process. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_WORKING 1 +#define STATE_UP 2 +#define STATE_WAITING 3 +#define STATE_WAITING_TERMINATING 4 +#define STATE_TERMINATING 5 + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + LinkedList0 clean_list; + LinkedList0 dirty_list; + int state; +}; + +struct join_instance { + NCDModuleInst *i; + struct instance *spawn; + LinkedList0Node list_node; + int is_dirty; +}; + +static void assert_dirty_state (struct instance *o); +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void bring_joins_down (struct instance *o); +static void continue_working (struct instance *o); +static void continue_terminating (struct instance *o); +static void instance_free (struct instance *o); + +static void assert_dirty_state (struct instance *o) +{ + ASSERT(!LinkedList0_IsEmpty(&o->dirty_list) == (o->state == STATE_WAITING || o->state == STATE_WAITING_TERMINATING)) +} + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + assert_dirty_state(o); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_WORKING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // set state up + o->state = STATE_UP; + + // bring joins up + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->clean_list); ln; ln = LinkedList0Node_Next(ln)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + NCDModuleInst_Backend_Up(join->i); + } + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_UP) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // bring joins down, moving them to the dirty list + bring_joins_down(o); + + // set state waiting + o->state = STATE_WAITING; + + // if we have no joins, continue immediately + if (LinkedList0_IsEmpty(&o->dirty_list)) { + continue_working(o); + return; + } + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // die finally + instance_free(o); + return; + } break; + + default: ASSERT(0); + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, caller_obj_func_getobj); + return 1; + } + + return 0; +} + +static int caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void bring_joins_down (struct instance *o) +{ + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->clean_list)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + NCDModuleInst_Backend_Down(join->i); + LinkedList0_Remove(&o->clean_list, &join->list_node); + LinkedList0_Prepend(&o->dirty_list, &join->list_node); + join->is_dirty = 1; + } +} + +static void continue_working (struct instance *o) +{ + ASSERT(o->state == STATE_WAITING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // continue process + NCDModuleProcess_Continue(&o->process); + + // set state working + o->state = STATE_WORKING; +} + +static void continue_terminating (struct instance *o) +{ + ASSERT(o->state == STATE_WAITING_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // request process to terminate + NCDModuleProcess_Terminate(&o->process); + + // set state terminating + o->state = STATE_TERMINATING; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // signal up. + // Do it before creating the process so that the process starts initializing before our own process continues. + NCDModuleInst_Backend_Up(o->i); + + // create process + if (!NCDModuleProcess_InitValue(&o->process, o->i, template_name_arg, args_arg, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set object resolution function + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + + // init lists + LinkedList0_Init(&o->clean_list); + LinkedList0_Init(&o->dirty_list); + + // set state working + o->state = STATE_WORKING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o) +{ + ASSERT(LinkedList0_IsEmpty(&o->dirty_list)) + + // unlink joins + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->clean_list)) { + struct join_instance *join = UPPER_OBJECT(ln, struct join_instance, list_node); + ASSERT(join->spawn == o) + ASSERT(!join->is_dirty) + LinkedList0_Remove(&o->clean_list, &join->list_node); + join->spawn = NULL; + } + + // free process + NCDModuleProcess_Free(&o->process); + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(o->state != STATE_WAITING_TERMINATING) + ASSERT(o->state != STATE_TERMINATING) + assert_dirty_state(o); + + // bring joins down + if (o->state == STATE_UP) { + bring_joins_down(o); + } + + // set state waiting terminating + o->state = STATE_WAITING_TERMINATING; + + // start terminating now if possible + if (LinkedList0_IsEmpty(&o->dirty_list)) { + continue_terminating(o); + return; + } +} + +static void join_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct join_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + o->spawn = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + assert_dirty_state(o->spawn); + + LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node); + o->is_dirty = 0; + + if (o->spawn->state == STATE_UP) { + NCDModuleInst_Backend_Up(i); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void join_func_die (void *vo) +{ + struct join_instance *o = vo; + + if (o->spawn) { + assert_dirty_state(o->spawn); + + // remove from list + if (o->is_dirty) { + LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node); + } else { + LinkedList0_Remove(&o->spawn->clean_list, &o->list_node); + } + + if (o->is_dirty && LinkedList0_IsEmpty(&o->spawn->dirty_list)) { + ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING) + + if (o->spawn->state == STATE_WAITING) { + continue_working(o->spawn); + } else { + continue_terminating(o->spawn); + } + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int join_func_getobj (void *vo, NCD_string_id_t name, NCDObject *out) +{ + struct join_instance *o = vo; + + if (!o->spawn) { + return 0; + } + + return NCDModuleProcess_GetObj(&o->spawn->process, name, out); +} + +static void join_func_clean (void *vo) +{ + struct join_instance *o = vo; + + if (!(o->spawn && o->is_dirty)) { + return; + } + + assert_dirty_state(o->spawn); + ASSERT(o->spawn->state == STATE_WAITING || o->spawn->state == STATE_WAITING_TERMINATING) + + LinkedList0_Remove(&o->spawn->dirty_list, &o->list_node); + LinkedList0_Prepend(&o->spawn->clean_list, &o->list_node); + o->is_dirty = 0; + + if (LinkedList0_IsEmpty(&o->spawn->dirty_list)) { + if (o->spawn->state == STATE_WAITING) { + continue_working(o->spawn); + } else { + continue_terminating(o->spawn); + } + } +} + +static struct NCDModule modules[] = { + { + .type = "spawn", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "synchronous_process", // deprecated name + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "spawn::join", + .func_new2 = join_func_new, + .func_die = join_func_die, + .func_getobj = join_func_getobj, + .func_clean = join_func_clean, + .alloc_size = sizeof(struct join_instance), + .flags = NCDMODULE_FLAG_CAN_RESOLVE_WHEN_DOWN + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_spawn = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/strcmp.c b/external/badvpn_dns/ncd/modules/strcmp.c new file mode 100644 index 00000000..e6487196 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/strcmp.c @@ -0,0 +1,107 @@ +/** + * @file strcmp.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * String comparison module. + * + * Synopsis: strcmp(string str1, string str2) + * Variables: + * string (empty) - "true" if str1 and str2 are equal, "false" if not + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int result; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef str1_arg; + NCDValRef str2_arg; + if (!NCDVal_ListRead(params->args, 2, &str1_arg, &str2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str1_arg) || !NCDVal_IsString(str2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // compare + o->result = (NCDVal_Compare(str1_arg, str2_arg) == 0); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->result, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "strcmp", + .func_new2 = func_new, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_strcmp = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/substr.c b/external/badvpn_dns/ncd/modules/substr.c new file mode 100644 index 00000000..6a50b0cc --- /dev/null +++ b/external/badvpn_dns/ncd/modules/substr.c @@ -0,0 +1,167 @@ +/** + * @file substr.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * substr(string str, string start [, string max]) + * + * Description: + * Extracts a substring from a string. The result is the longest substring which + * starts at the offset 'start' bytes into 'str', and is no longer than 'max' bytes. + * If 'max' is not provided, the result is the substring from the offset 'start' to + * the end. In any case, 'start' must not be greater than the length of 'str'. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct substr_instance { + NCDModuleInst *i; + const char *data; + size_t length; + int is_external; + BRefTarget *external_ref_target; +}; + +static void substr_func_new_common (void *vo, NCDModuleInst *i, const char *data, size_t length, int is_external, BRefTarget *external_ref_target) +{ + struct substr_instance *o = vo; + o->i = i; + + o->data = data; + o->length = length; + o->is_external = is_external; + o->external_ref_target = external_ref_target; + + NCDModuleInst_Backend_Up(i); +} + +static int substr_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct substr_instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + if (o->is_external) { + *out = NCDVal_NewExternalString(mem, o->data, o->length, o->external_ref_target); + } else { + *out = NCDVal_NewStringBin(mem, (const uint8_t *)o->data, o->length); + } + return 1; + } + + return 0; +} + +static void func_new_substr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef str_arg; + NCDValRef start_arg; + NCDValRef max_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &str_arg, &start_arg) && + !NCDVal_ListRead(params->args, 3, &str_arg, &start_arg, &max_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(str_arg) || !NCDVal_IsString(start_arg) || + (!NCDVal_IsInvalid(max_arg) && !NCDVal_IsString(max_arg)) + ) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t start; + if (!ncd_read_uintmax(start_arg, &start) || start > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong size"); + goto fail0; + } + + uintmax_t max = SIZE_MAX; + if (!NCDVal_IsInvalid(max_arg)) { + if (!ncd_read_uintmax(max_arg, &max) || max > SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "wrong max"); + goto fail0; + } + } + + const char *str_data = NCDVal_StringData(str_arg); + size_t str_length = NCDVal_StringLength(str_arg); + + if (start > str_length) { + ModuleLog(i, BLOG_ERROR, "start is beyond the end of the string"); + goto fail0; + } + + const char *sub_data = str_data + start; + size_t sub_length = str_length - start; + if (sub_length > max) { + sub_length = max; + } + + int is_external = 0; + BRefTarget *external_ref_target = NULL; + + if (NCDVal_IsExternalString(str_arg)) { + is_external = 1; + external_ref_target = NCDVal_ExternalStringTarget(str_arg); + } + else if (NCDVal_IsIdString(str_arg)) { + is_external = 1; + } + + substr_func_new_common(vo, i, sub_data, sub_length, is_external, external_ref_target); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "substr", + .func_new2 = func_new_substr, + .func_getvar2 = substr_func_getvar, + .alloc_size = sizeof(struct substr_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_substr = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/sys_evdev.c b/external/badvpn_dns/ncd/modules/sys_evdev.c new file mode 100644 index 00000000..d848a898 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_evdev.c @@ -0,0 +1,348 @@ +/** + * @file sys_evdev.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Linux event device module. + * + * Synopsis: sys.evdev(string device) + * Description: reports input events from a Linux event device. Transitions up when an event is + * detected, and goes down waiting for the next event when sys.evdev::nextevent() is called. + * Variables: + * string type - symbolic event type (e.g. EV_KEY, EV_REL, EV_ABS), corresponding to + * (struct input_event).type, or "unknown" + * string value - event value (signed integer), equal to (struct input_event).value + * string code_numeric - numeric event code (unsigned integer), equal to + * (struct input_event).code + * string code - symbolic event code (e.g. KEY_ESC. KEY_1, KEY_2, BTN_LEFT), corrresponding + * to (struct input_event).code, or "unknown" + * + * Synopsis: sys.evdev::nextevent() + * Description: makes the evdev module transition down in order to report the next event. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "linux_input_names.h" + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + int evdev_fd; + BFileDescriptor bfd; + int processing; + struct input_event event; +}; + +static void instance_free (struct instance *o, int is_error); + +enum {STRING_VALUE, STRING_CODE_NUMERIC, STRING_CODE}; + +static const char *strings[] = { + "value", "code_numeric", "code", NULL +}; + +#define MAKE_LOOKUP_FUNC(_name_) \ +static const char * evdev_##_name_##_to_str (uint16_t type) \ +{ \ + if (type >= (sizeof(_name_##_names) / sizeof(_name_##_names[0])) || !_name_##_names[type]) { \ + return "unknown"; \ + } \ + return _name_##_names[type]; \ +} + +MAKE_LOOKUP_FUNC(type) +MAKE_LOOKUP_FUNC(key) +MAKE_LOOKUP_FUNC(rel) +MAKE_LOOKUP_FUNC(abs) +MAKE_LOOKUP_FUNC(sw) +MAKE_LOOKUP_FUNC(msc) +MAKE_LOOKUP_FUNC(led) +MAKE_LOOKUP_FUNC(rep) +MAKE_LOOKUP_FUNC(snd) +MAKE_LOOKUP_FUNC(ffstatus) + +static void device_handler (struct instance *o, int events) +{ + if (o->processing) { + ModuleLog(o->i, BLOG_ERROR, "device error"); + instance_free(o, 1); + return; + } + + int res = read(o->evdev_fd, &o->event, sizeof(o->event)); + if (res < 0) { + ModuleLog(o->i, BLOG_ERROR, "read failed"); + instance_free(o, 1); + return; + } + if (res != sizeof(o->event)) { + ModuleLog(o->i, BLOG_ERROR, "read wrong"); + instance_free(o, 1); + return; + } + + // stop reading + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, 0); + + // set processing + o->processing = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void device_nextevent (struct instance *o) +{ + ASSERT(o->processing) + + // start reading + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + + // set not processing + o->processing = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef device_arg; + if (!NCDVal_ListRead(params->args, 1, &device_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(device_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate device + NCDValNullTermString device_nts; + if (!NCDVal_StringNullTerminate(device_arg, &device_nts)) { + ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // open device + o->evdev_fd = open(device_nts.data, O_RDONLY); + NCDValNullTermString_Free(&device_nts); + if (o->evdev_fd < 0) { + ModuleLog(o->i, BLOG_ERROR, "open failed"); + goto fail0; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->evdev_fd)) { + ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->evdev_fd, (BFileDescriptor_handler)device_handler, o); + if (!BReactor_AddFileDescriptor(o->i->params->iparams->reactor, &o->bfd)) { + ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + + // set not processing + o->processing = 0; + return; + +fail1: + if (close(o->evdev_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); + + // close device. + // Ignore close error which happens if the device is removed. + if (close(o->evdev_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->processing) + + if (name == NCD_STRING_TYPE) { + *out = NCDVal_NewString(mem, evdev_type_to_str(o->event.type)); + return 1; + } + + if (name == ModuleString(o->i, STRING_VALUE)) { + char str[50]; + snprintf(str, sizeof(str), "%"PRIi32, o->event.value); + *out = NCDVal_NewString(mem, str); + return 1; + } + + if (name == ModuleString(o->i, STRING_CODE_NUMERIC)) { + *out = ncd_make_uintmax(mem, o->event.code); + return 1; + } + + if (name == ModuleString(o->i, STRING_CODE)) { + const char *str = "unknown"; + + #define MAKE_CASE(_evname_, _name_) \ + case _evname_: \ + str = evdev_##_name_##_to_str(o->event.code); \ + break; + + switch (o->event.type) { + #ifdef EV_KEY + MAKE_CASE(EV_KEY, key) + #endif + #ifdef EV_REL + MAKE_CASE(EV_REL, rel) + #endif + #ifdef EV_ABS + MAKE_CASE(EV_ABS, abs) + #endif + #ifdef EV_SW + MAKE_CASE(EV_SW, sw) + #endif + #ifdef EV_MSC + MAKE_CASE(EV_MSC, msc) + #endif + #ifdef EV_LED + MAKE_CASE(EV_LED, led) + #endif + #ifdef EV_REP + MAKE_CASE(EV_REP, rep) + #endif + #ifdef EV_SND + MAKE_CASE(EV_SND, snd) + #endif + #ifdef EV_FF_STATUS + MAKE_CASE(EV_FF_STATUS, ffstatus) + #endif + } + + *out = NCDVal_NewString(mem, str); + return 1; + } + + return 0; +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!mo->processing) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + device_nextevent(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.evdev", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.evdev::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_evdev = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/sys_request_client.c b/external/badvpn_dns/ncd/modules/sys_request_client.c new file mode 100644 index 00000000..fe4a54ce --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_request_client.c @@ -0,0 +1,646 @@ +/** + * @file sys_request_client.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.request_client(string connect_addr) + * + * Description: + * Connects to a request server (sys.request_server()). + * Goes up when the connection, and dies with error when it is broken. + * When requested to die, dies immediately, breaking the connection. + * + * The connect address should be in the same format as for the socket module. + * In particular, it must be in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * + * Synopsis: + * sys.request_client::request(request_data, string reply_handler, string finished_handler, list args) + * + * Description: + * Sends a request to the server and dispatches replies to the provided handlers. + * + * The 'request_data' argument is sent as part of the request and is used by the server + * to determine what to do with the request. + * + * When a reply is received, a new template process is created from 'reply_handler' to process the + * reply. This process can access the reply data sent by the server using '_reply.data'. + * Similarly, if the server finishes the request, a process is created from 'finished_handler'. + * In both cases, the process can access objects as seen from the request statement via "_caller". + * Termination of these processes is initiated immediately after they completes. They are created + * synchronously - if a reply or a finished message arrives before a previous process is has + * finished, it is queued. Once the finished message has been processed by 'finished_handler', no + * more processes will be created. + * + * When the request statement is requested to terminate, it initiates termination of the current + * handler process and waits for it to terminate (if any is running), and then dies. + * If the corresponding client statement dies after being requested to die, or as a result of + * an error, the request statement will not react to this. It will dispatch any pending messages + * and then proceed to do nothing. In this case, if a finished message was not received, it will + * not be dispatched. + * + * The request statement may however die at any time due to errors. In this case, it will + * initiate termination of the current process and wait for it to terminate (if any) before dying. + * + * The request protocol and the server allow the client the abort requests at any time, and to + * have the client notified only after the request has been completely aborted (i.e. the handler + * process of sys.request_server() has deinitialized completely). This client implementation will + * automatically request abortion of active requests when the request statement is requested + * to die. However, the request statement will not wait for the abortion to finish before dying. + * This means, for instance, that if you initialize a request statement right after having + * deinitiazed it, the requests may overlap on the server side. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define CSTATE_CONNECTING 1 +#define CSTATE_CONNECTED 2 + +#define RRSTATE_SENDING_REQUEST 1 +#define RRSTATE_READY 2 +#define RRSTATE_GONE_BAD 3 +#define RRSTATE_GONE_GOOD 4 + +#define RPSTATE_NONE 1 +#define RPSTATE_WORKING 2 +#define RPSTATE_TERMINATING 3 + +#define RDSTATE_NONE 1 +#define RDSTATE_DYING 2 +#define RDSTATE_DYING_ERROR 3 + +struct instance { + NCDModuleInst *i; + NCDRequestClient client; + LinkedList0 requests_list; + int state; +}; + +struct request_instance { + NCDModuleInst *i; + NCDValRef reply_handler; + NCDValRef finished_handler; + NCDValRef args; + struct instance *client; + NCDRequestClientRequest request; + LinkedList0Node requests_list_node; + LinkedList1 replies_list; + NCDModuleProcess process; + int process_is_finished; + NCDValMem process_reply_mem; + NCDValRef process_reply_data; + int rstate; + int pstate; + int dstate; +}; + +struct reply { + LinkedList1Node replies_list_node; + NCDValMem mem; + NCDValRef val; +}; + +static void client_handler_error (struct instance *o); +static void client_handler_connected (struct instance *o); +static void request_handler_sent (struct request_instance *o); +static void request_handler_reply (struct request_instance *o, NCDValMem reply_mem, NCDValRef reply_value); +static void request_handler_finished (struct request_instance *o, int is_error); +static void request_process_handler_event (NCDModuleProcess *process, int event); +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int request_process_reply_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out); +static void request_gone (struct request_instance *o, int is_bad); +static void request_terminate_process (struct request_instance *o); +static void request_die (struct request_instance *o, int is_error); +static void request_free_reply (struct request_instance *o, struct reply *r, int have_value); +static int request_init_reply_process (struct request_instance *o, NCDValMem reply_mem, NCDValSafeRef reply_data); +static int request_init_finished_process (struct request_instance *o); +static void instance_free (struct instance *o, int with_error); +static void request_instance_free (struct request_instance *o, int with_error); + +enum {STRING_REPLY, STRING_DATA}; + +static const char *strings[] = { + "_reply", "data", NULL +}; + +static void client_handler_error (struct instance *o) +{ + ModuleLog(o->i, BLOG_ERROR, "client error"); + + // free instance + instance_free(o, 1); +} + +static void client_handler_connected (struct instance *o) +{ + ASSERT(o->state == CSTATE_CONNECTING) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state connected + o->state = CSTATE_CONNECTED; +} + +static void request_handler_sent (struct request_instance *o) +{ + ASSERT(o->rstate == RRSTATE_SENDING_REQUEST) + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set rstate ready + o->rstate = RRSTATE_READY; +} + +static void request_handler_reply (struct request_instance *o, NCDValMem reply_mem, NCDValRef reply_value) +{ + ASSERT(o->rstate == RRSTATE_READY) + + // queue reply if process is running + if (o->pstate != RPSTATE_NONE) { + struct reply *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail1; + } + r->mem = reply_mem; + r->val = NCDVal_Moved(&r->mem, reply_value); + LinkedList1_Append(&o->replies_list, &r->replies_list_node); + return; + } + + // start reply process + if (!request_init_reply_process(o, reply_mem, NCDVal_ToSafe(reply_value))) { + goto fail1; + } + + return; + +fail1: + NCDValMem_Free(&reply_mem); + request_die(o, 1); +} + +static void request_handler_finished (struct request_instance *o, int is_error) +{ + ASSERT(o->rstate == RRSTATE_SENDING_REQUEST || o->rstate == RRSTATE_READY) + ASSERT(is_error || o->rstate == RRSTATE_READY) + + if (is_error) { + ModuleLog(o->i, BLOG_ERROR, "received error reply"); + goto fail; + } + + // request gone good + request_gone(o, 0); + + // start process for reporting finished, if possible + if (o->pstate == RPSTATE_NONE) { + if (!request_init_finished_process(o)) { + goto fail; + } + } + + return; + +fail: + request_die(o, 1); +} + +static void request_process_handler_event (NCDModuleProcess *process, int event) +{ + struct request_instance *o = UPPER_OBJECT(process, struct request_instance, process); + ASSERT(o->pstate != RPSTATE_NONE) + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->pstate == RPSTATE_WORKING) + + // request process termination + request_terminate_process(o); + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(0) + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->pstate == RPSTATE_TERMINATING) + ASSERT(o->rstate != RRSTATE_SENDING_REQUEST) + + // free process + NCDModuleProcess_Free(&o->process); + + // free reply data + if (!o->process_is_finished) { + NCDValMem_Free(&o->process_reply_mem); + } + + // set process state none + o->pstate = RPSTATE_NONE; + + // die finally if requested + if (o->dstate == RDSTATE_DYING || o->dstate == RDSTATE_DYING_ERROR) { + request_instance_free(o, o->dstate == RDSTATE_DYING_ERROR); + return; + } + + if (!LinkedList1_IsEmpty(&o->replies_list)) { + // get first reply + struct reply *r = UPPER_OBJECT(LinkedList1_GetFirst(&o->replies_list), struct reply, replies_list_node); + + // start reply process + if (!request_init_reply_process(o, r->mem, NCDVal_ToSafe(r->val))) { + goto fail; + } + + // free reply + request_free_reply(o, r, 0); + } + else if (o->rstate == RRSTATE_GONE_GOOD && !o->process_is_finished) { + // start process for reporting finished + if (!request_init_finished_process(o)) { + goto fail; + } + } + + return; + + fail: + request_die(o, 1); + } break; + } +} + +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct request_instance *o = UPPER_OBJECT(process, struct request_instance, process); + ASSERT(o->pstate != RPSTATE_NONE) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, request_process_caller_obj_func_getobj); + return 1; + } + + if (!o->process_is_finished && name == ModuleString(o->i, STRING_REPLY)) { + *out_object = NCDObject_Build(-1, o, request_process_reply_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct request_instance *o = NCDObject_DataPtr(obj); + ASSERT(o->pstate != RPSTATE_NONE) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static int request_process_reply_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct request_instance *o = NCDObject_DataPtr(obj); + ASSERT(o->pstate != RPSTATE_NONE) + ASSERT(!o->process_is_finished) + + if (name == ModuleString(o->i, STRING_DATA)) { + *out = NCDVal_NewCopy(mem, o->process_reply_data); + return 1; + } + + return 0; +} + +static void request_gone (struct request_instance *o, int is_bad) +{ + ASSERT(o->rstate != RRSTATE_GONE_BAD) + ASSERT(o->rstate != RRSTATE_GONE_GOOD) + + // remove from requests list + LinkedList0_Remove(&o->client->requests_list, &o->requests_list_node); + + // free request + NCDRequestClientRequest_Free(&o->request); + + // set state over + o->rstate = (is_bad ? RRSTATE_GONE_BAD : RRSTATE_GONE_GOOD); +} + +static void request_terminate_process (struct request_instance *o) +{ + ASSERT(o->pstate == RPSTATE_WORKING) + + // request process termination + NCDModuleProcess_Terminate(&o->process); + + // set process state terminating + o->pstate = RPSTATE_TERMINATING; +} + +static void request_die (struct request_instance *o, int is_error) +{ + // if we have no process, die right away, else we have to wait for process to terminate + if (o->pstate == RPSTATE_NONE) { + request_instance_free(o, is_error); + return; + } + + // release request + if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) { + request_gone(o, 1); + } + + // initiate process termination, if needed + if (o->pstate != RPSTATE_TERMINATING) { + request_terminate_process(o); + } + + // set dstate + o->dstate = (is_error ? RDSTATE_DYING_ERROR : RDSTATE_DYING); +} + +static void request_free_reply (struct request_instance *o, struct reply *r, int have_value) +{ + // remove from replies list + LinkedList1_Remove(&o->replies_list, &r->replies_list_node); + + // free value + if (have_value) { + NCDValMem_Free(&r->mem); + } + + // free structure + free(r); +} + +static int request_init_reply_process (struct request_instance *o, NCDValMem reply_mem, NCDValSafeRef reply_data) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // set parameters + o->process_is_finished = 0; + o->process_reply_mem = reply_mem; + o->process_reply_data = NCDVal_FromSafe(&o->process_reply_mem, reply_data); + + // init process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->reply_handler, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&o->process, request_process_func_getspecialobj); + + // set process state working + o->pstate = RPSTATE_WORKING; + return 1; + +fail0: + return 0; +} + +static int request_init_finished_process (struct request_instance *o) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // set parameters + o->process_is_finished = 1; + + // init process + if (!NCDModuleProcess_InitValue(&o->process, o->i, o->finished_handler, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special objects function + NCDModuleProcess_SetSpecialFuncs(&o->process, request_process_func_getspecialobj); + + // set process state working + o->pstate = RPSTATE_WORKING; + return 1; + +fail0: + return 0; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef connect_addr_arg; + if (!NCDVal_ListRead(params->args, 1, &connect_addr_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // read connect address + struct BConnection_addr addr; + if (!ncd_read_bconnection_addr(connect_addr_arg, &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong connect address"); + goto fail0; + } + + // init client + if (!NCDRequestClient_Init(&o->client, addr, i->params->iparams->reactor, o, + (NCDRequestClient_handler_error)client_handler_error, + (NCDRequestClient_handler_connected)client_handler_connected)) { + ModuleLog(o->i, BLOG_ERROR, "NCDRequestClient_Init failed"); + goto fail0; + } + + // init requests list + LinkedList0_Init(&o->requests_list); + + // set state connecting + o->state = CSTATE_CONNECTING; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o, int with_error) +{ + // deal with requests + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&o->requests_list)) { + struct request_instance *req = UPPER_OBJECT(ln, struct request_instance, requests_list_node); + request_gone(req, 1); + } + + // free client + NCDRequestClient_Free(&o->client); + + if (with_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + instance_free(o, 0); +} + +static void request_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct request_instance *o = vo; + o->i = i; + + // check arguments + NCDValRef request_data_arg; + NCDValRef reply_handler_arg; + NCDValRef finished_handler_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 4, &request_data_arg, &reply_handler_arg, &finished_handler_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(reply_handler_arg) || !NCDVal_IsString(finished_handler_arg) || + !NCDVal_IsList(args_arg) + ) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->reply_handler = reply_handler_arg; + o->finished_handler = finished_handler_arg; + o->args = args_arg; + + // get client + struct instance *client = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + o->client = client; + + // check client state + if (client->state != CSTATE_CONNECTED) { + ModuleLog(o->i, BLOG_ERROR, "client is not connected"); + goto fail0; + } + + // init request + if (!NCDRequestClientRequest_Init(&o->request, &client->client, request_data_arg, o, + (NCDRequestClientRequest_handler_sent)request_handler_sent, + (NCDRequestClientRequest_handler_reply)request_handler_reply, + (NCDRequestClientRequest_handler_finished)request_handler_finished)) { + ModuleLog(o->i, BLOG_ERROR, "NCDRequestClientRequest_Init failed"); + goto fail0; + } + + // add to requests list + LinkedList0_Prepend(&client->requests_list, &o->requests_list_node); + + // init replies list + LinkedList1_Init(&o->replies_list); + + // set state + o->rstate = RRSTATE_SENDING_REQUEST; + o->pstate = RPSTATE_NONE; + o->dstate = RDSTATE_NONE; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void request_instance_free (struct request_instance *o, int with_error) +{ + ASSERT(o->pstate == RPSTATE_NONE) + + // free replies + LinkedList1Node *ln; + while (ln = LinkedList1_GetFirst(&o->replies_list)) { + struct reply *r = UPPER_OBJECT(ln, struct reply, replies_list_node); + request_free_reply(o, r, 1); + } + + // release request + if (o->rstate != RRSTATE_GONE_BAD && o->rstate != RRSTATE_GONE_GOOD) { + request_gone(o, 1); + } + + if (with_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void request_func_die (void *vo) +{ + struct request_instance *o = vo; + + request_die(o, 0); +} + +static struct NCDModule modules[] = { + { + .type = "sys.request_client", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.request_client::request", + .func_new2 = request_func_new, + .func_die = request_func_die, + .alloc_size = sizeof(struct request_instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_request_client = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/sys_request_server.c b/external/badvpn_dns/ncd/modules/sys_request_server.c new file mode 100644 index 00000000..31bc431d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_request_server.c @@ -0,0 +1,792 @@ +/** + * @file sys_request_server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A simple a IPC interface for NCD to talk to other processes over a Unix socket. + * + * Synopsis: + * sys.request_server(listen_address, string request_handler_template, list args) + * + * Description: + * Initializes a request server on the given socket path. Requests are served by + * starting a template process for every request. Multiple such processes may + * exist simultaneously. Termination of these processess may be initiated at + * any time if the request server no longer needs the request in question served. + * The payload of a request is a value, and can be accessed as _request.data + * from within the handler process. Replies to the request can be sent using + * _request->reply(data); replies are values too. Finally, _request->finish() + * should be called to indicate that no further replies will be sent. Calling + * finish() will immediately initiate termination of the handler process. + * Requests can be sent to NCD using the badvpn-ncd-request program. + * + * The listen address should be in the same format as for the socket module. + * In particular, it must be in one of the following forms: + * - {"tcp", {"ipv4", ipv4_address, port_number}}, + * - {"tcp", {"ipv6", ipv6_address, port_number}}, + * - {"unix", socket_path}. + * + * Predefined objects and variables in request_handler_template: + * _caller - provides access to objects as seen from the sys.request_server() + * command + * _request.data - the request payload as sent by the client + * string _request.client_addr - the address of the client. The form is + * like the second part of the sys.request_server() address format, e.g. + * {"ipv4", "1.2.3.4", "4000"}. + * + * Synopsis: + * sys.request_server.request::reply(reply_data) + * + * Synopsis: + * sys.request_server.request::finish() + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define SEND_PAYLOAD_MTU 32768 +#define RECV_PAYLOAD_MTU 32768 + +#define SEND_MTU (SEND_PAYLOAD_MTU + sizeof(struct requestproto_header)) +#define RECV_MTU (RECV_PAYLOAD_MTU + sizeof(struct requestproto_header)) + +struct instance { + NCDModuleInst *i; + NCDValRef request_handler_template; + NCDValRef args; + BListener listener; + LinkedList0 connections_list; + int dying; +}; + +#define CONNECTION_STATE_RUNNING 1 +#define CONNECTION_STATE_TERMINATING 2 + +struct reply; + +struct connection { + struct instance *inst; + LinkedList0Node connections_list_node; + BConnection con; + BAddr addr; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + PacketPassFifoQueue send_queue; + PacketStreamSender send_pss; + LinkedList0 requests_list; + LinkedList0 replies_list; + int state; +}; + +struct request { + struct connection *con; + uint32_t request_id; + LinkedList0Node requests_list_node; + NCDValMem request_data_mem; + NCDValRef request_data; + struct reply *end_reply; + NCDModuleProcess process; + int terminating; + int got_finished; +}; + +struct reply { + struct connection *con; + LinkedList0Node replies_list_node; + PacketPassFifoQueueFlow send_qflow; + PacketPassInterface *send_qflow_if; + uint8_t *send_buf; +}; + +static void listener_handler (struct instance *o); +static void connection_free (struct connection *c); +static void connection_free_link (struct connection *c); +static void connection_terminate (struct connection *c); +static void connection_con_handler (struct connection *c, int event); +static void connection_recv_decoder_handler_error (struct connection *c); +static void connection_recv_if_handler_send (struct connection *c, uint8_t *data, int data_len); +static int request_init (struct connection *c, uint32_t request_id, const uint8_t *data, int data_len); +static void request_free (struct request *r); +static struct request * find_request (struct connection *c, uint32_t request_id); +static void request_process_handler_event (NCDModuleProcess *process, int event); +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static int request_process_request_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out_value); +static void request_terminate (struct request *r); +static struct reply * reply_init (struct connection *c, uint32_t request_id, NCDValRef reply_data); +static void reply_start (struct reply *r, uint32_t type); +static void reply_free (struct reply *r); +static void reply_send_qflow_if_handler_done (struct reply *r); +static void instance_free (struct instance *o); + +enum {STRING_REQUEST, STRING_DATA, STRING_CLIENT_ADDR, + STRING_SYS_REQUEST_SERVER_REQUEST}; + +static const char *strings[] = { + "_request", "data", "client_addr", + "sys.request_server.request", NULL +}; + +static void listener_handler (struct instance *o) +{ + ASSERT(!o->dying) + + BReactor *reactor = o->i->params->iparams->reactor; + BPendingGroup *pg = BReactor_PendingGroup(reactor); + + struct connection *c = malloc(sizeof(*c)); + if (!c) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + c->inst = o; + + LinkedList0_Prepend(&o->connections_list, &c->connections_list_node); + + if (!BConnection_Init(&c->con, BConnection_source_listener(&o->listener, &c->addr), reactor, c, (BConnection_handler)connection_con_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + BConnection_SendAsync_Init(&c->con); + BConnection_RecvAsync_Init(&c->con); + StreamPassInterface *con_send_if = BConnection_SendAsync_GetIf(&c->con); + StreamRecvInterface *con_recv_if = BConnection_RecvAsync_GetIf(&c->con); + + PacketPassInterface_Init(&c->recv_if, RECV_MTU, (PacketPassInterface_handler_send)connection_recv_if_handler_send, c, pg); + + if (!PacketProtoDecoder_Init(&c->recv_decoder, con_recv_if, &c->recv_if, pg, c, (PacketProtoDecoder_handler_error)connection_recv_decoder_handler_error)) { + ModuleLog(o->i, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + PacketStreamSender_Init(&c->send_pss, con_send_if, PACKETPROTO_ENCLEN(SEND_MTU), pg); + + PacketPassFifoQueue_Init(&c->send_queue, PacketStreamSender_GetInput(&c->send_pss), pg); + + LinkedList0_Init(&c->requests_list); + + LinkedList0_Init(&c->replies_list); + + c->state = CONNECTION_STATE_RUNNING; + + ModuleLog(o->i, BLOG_INFO, "connection initialized"); + return; + +fail2: + PacketPassInterface_Free(&c->recv_if); + BConnection_RecvAsync_Free(&c->con); + BConnection_SendAsync_Free(&c->con); + BConnection_Free(&c->con); +fail1: + LinkedList0_Remove(&o->connections_list, &c->connections_list_node); + free(c); +fail0: + return; +} + +static void connection_free (struct connection *c) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_TERMINATING) + ASSERT(LinkedList0_IsEmpty(&c->requests_list)) + ASSERT(LinkedList0_IsEmpty(&c->replies_list)) + + LinkedList0_Remove(&o->connections_list, &c->connections_list_node); + free(c); +} + +static void connection_free_link (struct connection *c) +{ + PacketPassFifoQueue_PrepareFree(&c->send_queue); + + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&c->replies_list)) { + struct reply *r = UPPER_OBJECT(ln, struct reply, replies_list_node); + ASSERT(r->con == c) + reply_free(r); + } + + PacketPassFifoQueue_Free(&c->send_queue); + PacketStreamSender_Free(&c->send_pss); + PacketProtoDecoder_Free(&c->recv_decoder); + PacketPassInterface_Free(&c->recv_if); + BConnection_RecvAsync_Free(&c->con); + BConnection_SendAsync_Free(&c->con); + BConnection_Free(&c->con); +} + +static void connection_terminate (struct connection *c) +{ + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + for (LinkedList0Node *ln = LinkedList0_GetFirst(&c->requests_list); ln; ln = LinkedList0Node_Next(ln)) { + struct request *r = UPPER_OBJECT(ln, struct request, requests_list_node); + + if (!r->terminating) { + request_terminate(r); + } + } + + connection_free_link(c); + + c->state = CONNECTION_STATE_TERMINATING; + + if (LinkedList0_IsEmpty(&c->requests_list)) { + connection_free(c); + return; + } +} + +static void connection_con_handler (struct connection *c, int event) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + ModuleLog(o->i, BLOG_INFO, "connection closed"); + + connection_terminate(c); +} + +static void connection_recv_decoder_handler_error (struct connection *c) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + + ModuleLog(o->i, BLOG_ERROR, "decoder error"); + + connection_terminate(c); +} + +static void connection_recv_if_handler_send (struct connection *c, uint8_t *data, int data_len) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_MTU) + + PacketPassInterface_Done(&c->recv_if); + + if (data_len < sizeof(struct requestproto_header)) { + ModuleLog(o->i, BLOG_ERROR, "missing requestproto header"); + goto fail; + } + + struct requestproto_header header; + memcpy(&header, data, sizeof(header)); + uint32_t request_id = ltoh32(header.request_id); + uint32_t type = ltoh32(header.type); + + switch (type) { + case REQUESTPROTO_TYPE_CLIENT_REQUEST: { + if (find_request(c, request_id)) { + ModuleLog(o->i, BLOG_ERROR, "request with the same ID already exists"); + goto fail; + } + + if (!request_init(c, request_id, data + sizeof(header), data_len - sizeof(header))) { + goto fail; + } + } break; + + case REQUESTPROTO_TYPE_CLIENT_ABORT: { + struct request *r = find_request(c, request_id); + if (!r) { + // this is expected if we finish before we get the abort + return; + } + + if (!r->terminating) { + request_terminate(r); + } + } break; + + default: + ModuleLog(o->i, BLOG_ERROR, "invalid requestproto type"); + goto fail; + } + + return; + +fail: + connection_terminate(c); +} + +static int request_init (struct connection *c, uint32_t request_id, const uint8_t *data, int data_len) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + ASSERT(!find_request(c, request_id)) + ASSERT(data_len >= 0) + ASSERT(data_len <= RECV_PAYLOAD_MTU) + + struct request *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + r->con = c; + r->request_id = request_id; + + LinkedList0_Prepend(&c->requests_list, &r->requests_list_node); + + NCDValMem_Init(&r->request_data_mem); + + if (!NCDValParser_Parse((const char *)data, data_len, &r->request_data_mem, &r->request_data)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValParser_Parse failed"); + goto fail1; + } + + if (!(r->end_reply = reply_init(c, request_id, NCDVal_NewInvalid()))) { + goto fail1; + } + + if (!NCDModuleProcess_InitValue(&r->process, o->i, o->request_handler_template, o->args, request_process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail3; + } + + NCDModuleProcess_SetSpecialFuncs(&r->process, request_process_func_getspecialobj); + + r->terminating = 0; + r->got_finished = 0; + + ModuleLog(o->i, BLOG_INFO, "request initialized"); + return 1; + +fail3: + reply_free(r->end_reply); +fail1: + NCDValMem_Free(&r->request_data_mem); + LinkedList0_Remove(&c->requests_list, &r->requests_list_node); + free(r); +fail0: + return 0; +} + +static void request_free (struct request *r) +{ + struct connection *c = r->con; + NCDModuleProcess_AssertFree(&r->process); + + if (c->state != CONNECTION_STATE_TERMINATING) { + uint32_t type = r->got_finished ? REQUESTPROTO_TYPE_SERVER_FINISHED : REQUESTPROTO_TYPE_SERVER_ERROR; + reply_start(r->end_reply, type); + } + + NCDModuleProcess_Free(&r->process); + NCDValMem_Free(&r->request_data_mem); + LinkedList0_Remove(&c->requests_list, &r->requests_list_node); + free(r); +} + +static struct request * find_request (struct connection *c, uint32_t request_id) +{ + for (LinkedList0Node *ln = LinkedList0_GetFirst(&c->requests_list); ln; ln = LinkedList0Node_Next(ln)) { + struct request *r = UPPER_OBJECT(ln, struct request, requests_list_node); + if (!r->terminating && r->request_id == request_id) { + return r; + } + } + + return NULL; +} + +static void request_process_handler_event (NCDModuleProcess *process, int event) +{ + struct request *r = UPPER_OBJECT(process, struct request, process); + struct connection *c = r->con; + struct instance *o = c->inst; + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(!r->terminating) + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(!r->terminating) + + NCDModuleProcess_Continue(&r->process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(r->terminating) + + request_free(r); + + if (c->state == CONNECTION_STATE_TERMINATING && LinkedList0_IsEmpty(&c->requests_list)) { + connection_free(c); + + if (o->dying && LinkedList0_IsEmpty(&o->connections_list)) { + instance_free(o); + return; + } + } + } break; + + default: ASSERT(0); + } +} + +static int request_process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct request *r = UPPER_OBJECT(process, struct request, process); + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, r, NCDObject_no_getvar, request_process_caller_obj_func_getobj); + return 1; + } + + if (name == ModuleString(r->con->inst->i, STRING_REQUEST)) { + *out_object = NCDObject_Build(ModuleString(r->con->inst->i, STRING_SYS_REQUEST_SERVER_REQUEST), r, request_process_request_obj_func_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int request_process_caller_obj_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct request *r = NCDObject_DataPtr(obj); + + return NCDModuleInst_Backend_GetObj(r->con->inst->i, name, out_object); +} + +static int request_process_request_obj_func_getvar (const NCDObject *obj, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct request *r = NCDObject_DataPtr(obj); + + if (name == ModuleString(r->con->inst->i, STRING_DATA)) { + *out = NCDVal_NewCopy(mem, r->request_data); + return 1; + } + + if (name == ModuleString(r->con->inst->i, STRING_CLIENT_ADDR)) { + *out = ncd_make_baddr(r->con->addr, mem); + return 1; + } + + return 0; +} + +static void request_terminate (struct request *r) +{ + ASSERT(!r->terminating) + + NCDModuleProcess_Terminate(&r->process); + + r->terminating = 1; +} + +static struct reply * reply_init (struct connection *c, uint32_t request_id, NCDValRef reply_data) +{ + struct instance *o = c->inst; + ASSERT(c->state == CONNECTION_STATE_RUNNING) + NCDVal_Assert(reply_data); + + struct reply *r = malloc(sizeof(*r)); + if (!r) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + r->con = c; + + LinkedList0_Prepend(&c->replies_list, &r->replies_list_node); + + PacketPassFifoQueueFlow_Init(&r->send_qflow, &c->send_queue); + + r->send_qflow_if = PacketPassFifoQueueFlow_GetInput(&r->send_qflow); + PacketPassInterface_Sender_Init(r->send_qflow_if, (PacketPassInterface_handler_done)reply_send_qflow_if_handler_done, r); + + ExpString str; + if (!ExpString_Init(&str)) { + ModuleLog(o->i, BLOG_ERROR, "ExpString_Init failed"); + goto fail1; + } + + if (!ExpString_AppendZeros(&str, sizeof(struct packetproto_header) + sizeof(struct requestproto_header))) { + ModuleLog(o->i, BLOG_ERROR, "ExpString_AppendZeros failed"); + goto fail2; + } + + if (!NCDVal_IsInvalid(reply_data) && !NCDValGenerator_AppendGenerate(reply_data, &str)) { + ModuleLog(o->i, BLOG_ERROR, "NCDValGenerator_AppendGenerate failed"); + goto fail2; + } + + size_t len = ExpString_Length(&str); + if (len > INT_MAX || len > PACKETPROTO_ENCLEN(SEND_MTU) || len - sizeof(struct packetproto_header) > UINT16_MAX) { + ModuleLog(o->i, BLOG_ERROR, "reply is too long"); + goto fail2; + } + + r->send_buf = (uint8_t *)ExpString_Get(&str); + + struct packetproto_header pp; + pp.len = htol16(len - sizeof(pp)); + + struct requestproto_header rp; + rp.request_id = htol32(request_id); + + memcpy(r->send_buf, &pp, sizeof(pp)); + memcpy(r->send_buf + sizeof(pp), &rp, sizeof(rp)); + + return r; + +fail2: + ExpString_Free(&str); +fail1: + PacketPassFifoQueueFlow_Free(&r->send_qflow); + LinkedList0_Remove(&c->replies_list, &r->replies_list_node); + free(r); +fail0: + return NULL; +} + +static void reply_start (struct reply *r, uint32_t type) +{ + struct requestproto_header rp; + memcpy(&rp, r->send_buf + sizeof(struct packetproto_header), sizeof(rp)); + rp.type = htol32(type); + memcpy(r->send_buf + sizeof(struct packetproto_header), &rp, sizeof(rp)); + + struct packetproto_header pp; + memcpy(&pp, r->send_buf, sizeof(pp)); + + int len = ltoh16(pp.len) + sizeof(struct packetproto_header); + + PacketPassInterface_Sender_Send(r->send_qflow_if, r->send_buf, len); +} + +static void reply_free (struct reply *r) +{ + struct connection *c = r->con; + PacketPassFifoQueueFlow_AssertFree(&r->send_qflow); + + free(r->send_buf); + PacketPassFifoQueueFlow_Free(&r->send_qflow); + LinkedList0_Remove(&c->replies_list, &r->replies_list_node); + free(r); +} + +static void reply_send_qflow_if_handler_done (struct reply *r) +{ + reply_free(r); +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef listen_addr_arg; + NCDValRef request_handler_template_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 3, &listen_addr_arg, &request_handler_template_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(request_handler_template_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->request_handler_template = request_handler_template_arg; + o->args = args_arg; + + // read listen address + struct BConnection_addr addr; + if (!ncd_read_bconnection_addr(listen_addr_arg, &addr)) { + ModuleLog(o->i, BLOG_ERROR, "wrong listen address"); + goto fail0; + } + + // init listener + if (!BListener_InitGeneric(&o->listener, addr, i->params->iparams->reactor, o, (BListener_handler)listener_handler)) { + ModuleLog(o->i, BLOG_ERROR, "BListener_InitGeneric failed"); + goto fail0; + } + + // init connections list + LinkedList0_Init(&o->connections_list); + + // set not dying + o->dying = 0; + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + ASSERT(o->dying) + ASSERT(LinkedList0_IsEmpty(&o->connections_list)) + + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // free listener + BListener_Free(&o->listener); + + // terminate connections + LinkedList0Node *next_ln; + for (LinkedList0Node *ln = LinkedList0_GetFirst(&o->connections_list); ln && (next_ln = LinkedList0Node_Next(ln)), ln; ln = next_ln) { + struct connection *c = UPPER_OBJECT(ln, struct connection, connections_list_node); + ASSERT(c->inst == o) + + if (c->state != CONNECTION_STATE_TERMINATING) { + connection_terminate(c); + } + } + + // set dying + o->dying = 1; + + // if no connections, die right away + if (LinkedList0_IsEmpty(&o->connections_list)) { + instance_free(o); + return; + } +} + +static void reply_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef reply_data; + if (!NCDVal_ListRead(params->args, 1, &reply_data)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail; + } + + NCDModuleInst_Backend_Up(i); + + struct request *r = params->method_user; + struct connection *c = r->con; + + if (r->terminating) { + ModuleLog(i, BLOG_ERROR, "request is dying, cannot submit reply"); + goto fail; + } + + struct reply *rpl = reply_init(c, r->request_id, reply_data); + if (!rpl) { + ModuleLog(i, BLOG_ERROR, "failed to submit reply"); + goto fail; + } + + reply_start(rpl, REQUESTPROTO_TYPE_SERVER_REPLY); + return; + +fail: + NCDModuleInst_Backend_DeadError(i); +} + +static void finish_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail; + } + + NCDModuleInst_Backend_Up(i); + + struct request *r = params->method_user; + + if (r->terminating) { + ModuleLog(i, BLOG_ERROR, "request is dying, cannot submit finished"); + goto fail; + } + + r->got_finished = 1; + + request_terminate(r); + return; + +fail: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.request_server", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.request_server.request::reply", + .func_new2 = reply_func_new + }, { + .type = "sys.request_server.request::finish", + .func_new2 = finish_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_request_server = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/sys_start_process.c b/external/badvpn_dns/ncd/modules/sys_start_process.c new file mode 100644 index 00000000..f34e7b35 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_start_process.c @@ -0,0 +1,1266 @@ +/** + * @file sys_start_process.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * sys.start_process(list command, string mode [, map options]) + * + * Options: + * "keep_stdout":"true" - Start the program with the same stdout as the NCD process. + * Must not be present if the process is being opened for reading. + * "keep_stderr":true" - Start the program with the same stderr as the NCD process. + * "do_setsid":"true" - Call setsid() in the child before exec. This is needed to + * start the 'agetty' program. + * "username":username_string - Start the process under the permissions of the + * specified user. + * "term_on_deinit":"false" - do not send SIGTERM to the process when this statement + * is requested to terminate + * "deinit_kill_time":milliseconds - how long to wait for the process to terminate + * after this statement is requested to terminate until we send SIGKILL. If this option + * is not present or is "never", SIGKILL will not be sent. If this option is empty, the + * process will be sent SIGKILL immediately when the statement is requested to terminate. + * + * Variables: + * is_error - "true" if there was an error starting the process, "false" if the process + * has been started successfully + * + * Synopsis: + * sys.start_process::wait() + * + * Variables: + * exit_status - the exit code if the process terminated normally, -1 if it terminated + * with a signal + * + * Synopsis: + * sys.start_process::terminate() + * sys.start_process::kill() + * + * Synopsis: + * sys.start_process::read_pipe() + * + * Description: + * Creates a read interface to the process's standard output. Data is read using the + * read() method on this object. Read errors are reported implicitly by this statement + * going down and the 'is_error' variable changing to "true". + * When read_pipe() is initialized for a process, it takes ownership of the read pipe + * to the process. When read_pipe() is requested to terminate, it will close the pipe. + * Attempting to initialize read_pipe() on a process which was not started with 'r' + * in the mode argument, or where another read_pipe() object has already taken ownership + * of the read pipe, will result in throwing an error to the interpreter. + * + * Variables: + * string is_error - "true" if there was a read error, "false" if not + * + * Synopsis: + * sys.start_process::read_pipe::read() + * + * Description: + * Reads some data. If a read error occurs, it is reported implicitly via the + * read_pipe() object going down. If end of file is reached, this and any future read() + * operations will indicate that via the 'not_eof' variable. It is guaranteed that after + * EOF is reached, the read_pipe() object will not go down to report any errors. + * WARNING: if a read() is requested to terminate before it has completed, the + * read_pipe() will become unusable and any read() invocation after that will + * throw an error to the interpreter. + * + * Variables: + * string (empty) - data that was read, or an empty string on EOF + * string not_eof - "true" is EOF was not reached, "false" if it was + * + * Synopsis: + * sys.start_process::write_pipe() + * + * Description: + * Creates a write interface to the process's standard input. Data is written using the + * write() method on this object. Write errors are reported implicitly by this statement + * going down and the ''is_error variable changing to "true". + * When write_pipe() is initialized for a process, it takes ownership of the write pipe + * to the process. When write_pipe() is requested to terminate, it will close the pipe + * (unless the close() has been used). + * Attempting to initialize write_pipe() on a process which was not started with 'w' + * in the mode argument, or where another write_pipe() object has already taken ownership + * of the write pope, will result in throwing an error to the interpreter. + * + * Variables: + * string is_error - "true" if there was a write error, "false" if not + * + * Synopsis: + * sys.start_process::write_pipe::write(string data) + * + * Description: + * Writes the given data. If a write error occurs, it is reported implicitly via the + * write_pipe() object going down. + * WARNING: if a write() is requested to terminate before it has completed, the + * write_pipe() will become unusable and any write() or close() invocation after + * that will throw an error to the interpreter. + * + * Synopsis: + * sys.start_process::write_pipe::close(string data) + * + * Description: + * Closes the write pipe. This will make whatever is reading the other end of the pipe + * encounter EOF after it has read any pending data. It is guaranteed that after the + * pipe is closed, the write_pipe() object will not go down to report any errors. + * After close() is performed, any further write() or close() calls are disallowed and + * will throw errors to the interpreter. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define READ_BUF_SIZE 8192 + +#define PROCESS_STATE_ERROR 1 +#define PROCESS_STATE_RUNNING 2 +#define PROCESS_STATE_TERMINATED 3 +#define PROCESS_STATE_DYING 4 + +#define READER_STATE_RUNNING 1 +#define READER_STATE_EOF 2 +#define READER_STATE_ERROR 3 +#define READER_STATE_ABORTED 4 + +#define WRITER_STATE_RUNNING 1 +#define WRITER_STATE_CLOSED 2 +#define WRITER_STATE_ERROR 3 +#define WRITER_STATE_ABORTED 4 + +struct process_instance { + NCDModuleInst *i; + BProcess process; + BSmallTimer kill_timer; + LinkedList0 waits_list; + btime_t deinit_kill_time; + int term_on_deinit; + int read_fd; + int write_fd; + int exit_status; + int state; +}; + +struct wait_instance { + NCDModuleInst *i; + struct process_instance *pinst; + LinkedList0Node waits_list_node; + int exit_status; +}; + +struct read_pipe_instance { + NCDModuleInst *i; + int state; + int read_fd; + BConnection connection; + NCDBufStore store; + struct read_instance *read_inst; +}; + +struct read_instance { + NCDModuleInst *i; + struct read_pipe_instance *read_pipe_inst; + NCDBuf *buf; + size_t read_size; +}; + +struct write_pipe_instance { + NCDModuleInst *i; + int state; + int write_fd; + BConnection connection; + struct write_instance *write_inst; +}; + +struct write_instance { + NCDModuleInst *i; + struct write_pipe_instance *write_pipe_inst; + b_cstring cstr; + size_t pos; +}; + +static int parse_mode (NCDModuleInst *i, NCDValRef mode_arg, int *out_read, int *out_write) +{ + if (!NCDVal_IsString(mode_arg)) { + ModuleLog(i, BLOG_ERROR, "mode argument must be a string"); + return 0; + } + + *out_read = 0; + *out_write = 0; + + b_cstring cstr = NCDVal_StringCstring(mode_arg); + + B_CSTRING_LOOP_CHARS(cstr, char_pos, ch, { + if (ch == 'r') { + *out_read = 1; + } + else if (ch == 'w') { + *out_write = 1; + } + else { + ModuleLog(i, BLOG_ERROR, "invalid character in mode argument"); + return 0; + } + }) + + return 1; +} + +static void process_free (struct process_instance *o) +{ + // close write fd + if (o->write_fd != -1) { + if (close(o->write_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + } + + // close read fd + if (o->read_fd != -1) { + if (close(o->read_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void process_handler (void *vo, int normally, uint8_t normally_exit_status) +{ + struct process_instance *o = vo; + ASSERT(o->state == PROCESS_STATE_RUNNING || o->state == PROCESS_STATE_DYING) + + ModuleLog(o->i, BLOG_INFO, "process terminated"); + + // free kill timer + BReactor_RemoveSmallTimer(o->i->params->iparams->reactor, &o->kill_timer); + + // free process + BProcess_Free(&o->process); + + // remember exit code + o->exit_status = (!normally ? -1 : normally_exit_status); + + // finish waits + LinkedList0Node *ln; + while ((ln = LinkedList0_GetFirst(&o->waits_list))) { + struct wait_instance *winst = UPPER_OBJECT(ln, struct wait_instance, waits_list_node); + ASSERT(winst->pinst == o) + LinkedList0_Remove(&o->waits_list, &winst->waits_list_node); + winst->pinst = NULL; + winst->exit_status = o->exit_status; + NCDModuleInst_Backend_Up(winst->i); + } + + // if we have been requested to die, then die now + if (o->state == PROCESS_STATE_DYING) { + process_free(o); + return; + } + + // set state + o->state = PROCESS_STATE_TERMINATED; +} + +static void process_kill_timer_handler (BSmallTimer *kill_timer) +{ + struct process_instance *o = UPPER_OBJECT(kill_timer, struct process_instance, kill_timer); + ASSERT(o->state == PROCESS_STATE_DYING) + + ModuleLog(o->i, BLOG_INFO, "killing process after timeout"); + BProcess_Kill(&o->process); +} + +static int opts_func_unknown (void *user, NCDValRef key, NCDValRef val) +{ + struct process_instance *o = user; + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "term_on_deinit")) { + o->term_on_deinit = ncd_read_boolean(val); + return 1; + } + + if (NCDVal_IsString(key) && NCDVal_StringEquals(key, "deinit_kill_time")) { + if (NCDVal_StringEquals(val, "never")) { + o->deinit_kill_time = -2; + } + else if (NCDVal_StringEqualsId(val, NCD_STRING_EMPTY, o->i->params->iparams->string_index)) { + o->deinit_kill_time = -1; + } + else if (!ncd_read_time(val, &o->deinit_kill_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong value for deinit_kill_time option"); + return 0; + } + return 1; + } + + return 0; +} + +static void process_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct process_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + // check arguments + NCDValRef command_arg; + NCDValRef mode_arg; + NCDValRef options_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 2, &command_arg, &mode_arg) && + !NCDVal_ListRead(params->args, 3, &command_arg, &mode_arg, &options_arg) + ) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // parse mode + int is_read; + int is_write; + if (!parse_mode(i, mode_arg, &is_read, &is_write)) { + goto fail0; + } + + // parse options + NCDBProcessOpts opts; + int keep_stdout; + int keep_stderr; + o->deinit_kill_time = -2; + o->term_on_deinit = 1; + if (!NCDBProcessOpts_Init2(&opts, options_arg, opts_func_unknown, o, i, BLOG_CURRENT_CHANNEL, &keep_stdout, &keep_stderr)) { + goto fail0; + } + + // keep-stdout option and read mode are not compatible + if (keep_stdout && is_read) { + ModuleLog(i, BLOG_ERROR, "keep-stdout and read mode are not compatible"); + goto fail1; + } + + // prepare for creating pipes + int fds[4]; + int fds_map[3]; + int start_num_fds = opts.nfds; + int num_fds = start_num_fds; + memcpy(fds, opts.fds, num_fds * sizeof(int)); + memcpy(fds_map, opts.fds_map, num_fds * sizeof(int)); + int read_fd = -1; + int write_fd = -1; + + // create read pipe + if (is_read) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + ModuleLog(i, BLOG_ERROR, "pipe failed"); + goto error1; + } + read_fd = pipefd[0]; + fds[num_fds] = pipefd[1]; + fds_map[num_fds++] = STDOUT_FILENO; + } + + // create write pipe + if (is_write) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + ModuleLog(i, BLOG_ERROR, "pipe failed"); + goto error1; + } + write_fd = pipefd[1]; + fds[num_fds] = pipefd[0]; + fds_map[num_fds++] = STDIN_FILENO; + } + + // terminate fds array + fds[num_fds] = -1; + + // build process parameters struct + struct BProcess_params p_params = {}; + p_params.fds = fds; + p_params.fds_map = fds_map; + p_params.do_setsid = opts.do_setsid; + p_params.username = opts.username; + + // build command line + char *exec; + CmdLine cl; + if (!ncd_build_cmdline(i, BLOG_CURRENT_CHANNEL, command_arg, &exec, &cl)) { + goto error1; + } + + // start process + int res = BProcess_Init2(&o->process, i->params->iparams->manager, process_handler, o, exec, CmdLine_Get(&cl), p_params); + CmdLine_Free(&cl); + free(exec); + if (!res) { + ModuleLog(i, BLOG_ERROR, "BProcess_Init failed"); + goto error1; + } + + // init kill timer + BSmallTimer_Init(&o->kill_timer, process_kill_timer_handler); + + // close child fds + while (num_fds-- > start_num_fds) { + if (close(fds[num_fds]) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + + // free opts + NCDBProcessOpts_Free(&opts); + + // init waits list + LinkedList0_Init(&o->waits_list); + + // remember our fds + o->read_fd = read_fd; + o->write_fd = write_fd; + + // set state + o->state = PROCESS_STATE_RUNNING; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDBProcessOpts_Free(&opts); +fail0: + NCDModuleInst_Backend_DeadError(i); + return; + +error1: + if (write_fd != -1) { + if (close(write_fd) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + if (read_fd != -1) { + if (close(read_fd) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + while (num_fds-- > start_num_fds) { + if (close(fds[num_fds]) < 0) { + ModuleLog(i, BLOG_ERROR, "close failed"); + } + } + NCDBProcessOpts_Free(&opts); + + o->read_fd = -1; + o->write_fd = -1; + o->state = PROCESS_STATE_ERROR; + NCDModuleInst_Backend_Up(i); +} + +static void process_func_die (void *vo) +{ + struct process_instance *o = vo; + ASSERT(o->state != PROCESS_STATE_DYING) + + // if process is not running, die immediately + if (o->state != PROCESS_STATE_RUNNING) { + process_free(o); + return; + } + + if (o->term_on_deinit) { + ModuleLog(o->i, BLOG_INFO, "terminating process"); + + // send termination signal + BProcess_Terminate(&o->process); + } else { + ModuleLog(o->i, BLOG_INFO, "not terminating process as requested"); + } + + if (o->deinit_kill_time == -1) { + // user wants SIGKILL immediately + ModuleLog(o->i, BLOG_INFO, "killing process immediately"); + BProcess_Kill(&o->process); + } else if (o->deinit_kill_time >= 0) { + // user wants SIGKILL after some time + BReactor_SetSmallTimer(o->i->params->iparams->reactor, &o->kill_timer, BTIMER_SET_RELATIVE, o->deinit_kill_time); + } + + // set state + o->state = PROCESS_STATE_DYING; +} + +static int process_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct process_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == PROCESS_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void wait_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct wait_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->state == PROCESS_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "wait() is disallowed after the process has failed to start"); + goto fail0; + } + + if (pinst->state == PROCESS_STATE_TERMINATED) { + // not waiting, set no pinst + o->pinst = NULL; + + // remember exit code + o->exit_status = pinst->exit_status; + + // go up + NCDModuleInst_Backend_Up(i); + } else { + // waitint, set pinst + o->pinst = pinst; + + // insert to waits list + LinkedList0_Prepend(&pinst->waits_list, &o->waits_list_node); + } + + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void wait_func_die (void *vo) +{ + struct wait_instance *o = vo; + + // remove from waits list + if (o->pinst) { + LinkedList0_Remove(&o->pinst->waits_list, &o->waits_list_node); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int wait_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct wait_instance *o = vo; + ASSERT(!o->pinst) + + if (name == NCD_STRING_EXIT_STATUS) { + if (o->exit_status == -1) { + *out = NCDVal_NewString(mem, "-1"); + } else { + *out = ncd_make_uintmax(mem, o->exit_status); + } + return 1; + } + + return 0; +} + +static void terminate_kill_new_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_kill) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->state == PROCESS_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "terminate()/kill() is disallowed after the process has failed to start"); + goto fail0; + } + + if (pinst->state != PROCESS_STATE_TERMINATED) { + if (is_kill) { + BProcess_Kill(&pinst->process); + } else { + BProcess_Terminate(&pinst->process); + } + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void terminate_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + terminate_kill_new_common(vo, i, params, 0); +} + +static void kill_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + terminate_kill_new_common(vo, i, params, 1); +} + +static void read_pipe_free_connection (struct read_pipe_instance *o) +{ + // disconnect read instance + if (o->read_inst) { + ASSERT(o->read_inst->read_pipe_inst == o) + o->read_inst->read_pipe_inst = NULL; + } + + // free store + NCDBufStore_Free(&o->store); + + // free connection read interface + BConnection_RecvAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // close fd + if (close(o->read_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +} + +static void read_pipe_abort (struct read_pipe_instance *o) +{ + ASSERT(o->state == READER_STATE_RUNNING) + + // release connection resources + read_pipe_free_connection(o); + + // set state + o->state = READER_STATE_ABORTED; +} + +static void read_pipe_connection_handler (void *vo, int event) +{ + struct read_pipe_instance *o = vo; + ASSERT(o->state == READER_STATE_RUNNING) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + // if we have read operation, make it finish with eof + if (o->read_inst) { + ASSERT(o->read_inst->read_pipe_inst == o) + ASSERT(o->read_inst->buf) + o->read_inst->read_pipe_inst = NULL; + o->read_inst->read_size = 0; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; + } + + // free connection resources + read_pipe_free_connection(o); + + // set state closed + o->state = READER_STATE_EOF; + return; + } + + ModuleLog(o->i, BLOG_ERROR, "read pipe error"); + + // free connection resources + read_pipe_free_connection(o); + + // set state error + o->state = READER_STATE_ERROR; + + // backtrack + NCDModuleInst_Backend_DownUp(o->i); +} + +static void read_pipe_recv_handler_done (void *vo, int data_len) +{ + struct read_pipe_instance *o = vo; + ASSERT(o->state == READER_STATE_RUNNING) + ASSERT(o->read_inst) + ASSERT(o->read_inst->read_pipe_inst == o) + ASSERT(o->read_inst->buf) + ASSERT(data_len > 0) + ASSERT(data_len <= NCDBufStore_BufSize(&o->store)) + + // finish read operation + o->read_inst->read_pipe_inst = NULL; + o->read_inst->read_size = data_len; + NCDModuleInst_Backend_Up(o->read_inst->i); + o->read_inst = NULL; +} + +static void read_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_pipe_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->read_fd == -1) { + ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for reading or a read_pipe was already created"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->read_fd), i->params->iparams->reactor, o, read_pipe_connection_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection read interface + BConnection_RecvAsync_Init(&o->connection); + + // set recv done callback + StreamRecvInterface_Receiver_Init(BConnection_RecvAsync_GetIf(&o->connection), read_pipe_recv_handler_done, o); + + // init store + NCDBufStore_Init(&o->store, READ_BUF_SIZE); + + // set variables + o->state = READER_STATE_RUNNING; + o->read_fd = pinst->read_fd; + o->read_inst = NULL; + + // steal read fd from process instance + pinst->read_fd = -1; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_pipe_func_die (void *vo) +{ + struct read_pipe_instance *o = vo; + + // free connection resources + if (o->state == READER_STATE_RUNNING) { + read_pipe_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_pipe_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == READER_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void read_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct read_instance *o = vo; + o->i = i; + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct read_pipe_instance *read_pipe_inst = params->method_user; + + // check if a read error has already occured + if (read_pipe_inst->state == READER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read error has occured"); + goto fail0; + } + + // check if the read_pipe has been aborted + if (read_pipe_inst->state == READER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed after a read() has been aborted"); + goto fail0; + } + + // if EOF has already been encountered, complete the read immediately + if (read_pipe_inst->state == READER_STATE_EOF) { + o->buf = NULL; + o->read_pipe_inst = NULL; + o->read_size = 0; + NCDModuleInst_Backend_Up(i); + return; + } + + ASSERT(read_pipe_inst->state == READER_STATE_RUNNING) + + // check if there's already a read in progress + if (read_pipe_inst->read_inst) { + ModuleLog(i, BLOG_ERROR, "read() is disallowed while another read() is in progress"); + goto fail0; + } + + // get buffer + o->buf = NCDBufStore_GetBuf(&read_pipe_inst->store); + if (!o->buf) { + ModuleLog(i, BLOG_ERROR, "NCDBufStore_GetBuf failed"); + goto fail0; + } + + // set read_pipe + o->read_pipe_inst = read_pipe_inst; + + // register read in read_pipe + read_pipe_inst->read_inst = o; + + // receive + size_t buf_size = NCDBufStore_BufSize(&read_pipe_inst->store); + int to_read = (buf_size > INT_MAX ? INT_MAX : buf_size); + StreamRecvInterface_Receiver_Recv(BConnection_RecvAsync_GetIf(&read_pipe_inst->connection), (uint8_t *)NCDBuf_Data(o->buf), to_read); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void read_func_die (void *vo) +{ + struct read_instance *o = vo; + + // if we're receiving, abort read_pipe + if (o->read_pipe_inst) { + ASSERT(o->read_pipe_inst->state == READER_STATE_RUNNING) + ASSERT(o->read_pipe_inst->read_inst == o) + ASSERT(o->buf) + read_pipe_abort(o->read_pipe_inst); + } + + // release buffer + if (o->buf) { + BRefTarget_Deref(NCDBuf_RefTarget(o->buf)); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int read_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct read_instance *o = vo; + ASSERT(!o->read_pipe_inst) + ASSERT(!(o->read_size > 0) || o->buf) + + if (name == NCD_STRING_EMPTY) { + if (o->read_size > 0) { + *out = NCDVal_NewExternalString(mem, NCDBuf_Data(o->buf), o->read_size, NCDBuf_RefTarget(o->buf)); + } else { + *out = NCDVal_NewIdString(mem, NCD_STRING_EMPTY, o->i->params->iparams->string_index); + } + return 1; + } + + if (name == NCD_STRING_NOT_EOF) { + int not_eof = (o->read_size > 0); + *out = ncd_make_boolean(mem, not_eof, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_pipe_free_connection (struct write_pipe_instance *o) +{ + // disconnect write instance + if (o->write_inst) { + ASSERT(o->write_inst->write_pipe_inst == o) + o->write_inst->write_pipe_inst = NULL; + } + + // free connection send interface + BConnection_SendAsync_Free(&o->connection); + + // free connection + BConnection_Free(&o->connection); + + // close fd + if (close(o->write_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +} + +static void write_pipe_abort (struct write_pipe_instance *o) +{ + ASSERT(o->state == WRITER_STATE_RUNNING) + + // release connection resources + write_pipe_free_connection(o); + + // set state + o->state = WRITER_STATE_ABORTED; +} + +static void write_pipe_close (struct write_pipe_instance *o) +{ + ASSERT(o->state == WRITER_STATE_RUNNING) + + // release connection resources + write_pipe_free_connection(o); + + // set state + o->state = WRITER_STATE_CLOSED; +} + +static void write_pipe_connection_handler (void *vo, int event) +{ + struct write_pipe_instance *o = vo; + ASSERT(o->state == WRITER_STATE_RUNNING) + + ModuleLog(o->i, BLOG_ERROR, "write pipe error"); + + // free connection resources + write_pipe_free_connection(o); + + // set state error + o->state = WRITER_STATE_ERROR; + + // backtrack + NCDModuleInst_Backend_DownUp(o->i); +} + +static void write_pipe_send_handler_done (void *vo, int data_len) +{ + struct write_pipe_instance *o = vo; + ASSERT(o->state == WRITER_STATE_RUNNING) + ASSERT(o->write_inst) + ASSERT(o->write_inst->write_pipe_inst == o) + ASSERT(data_len > 0) + ASSERT(data_len <= o->write_inst->cstr.length - o->write_inst->pos) + + struct write_instance *wr = o->write_inst; + + // update write progress + wr->pos += data_len; + + // if there is more data, start another write operation + if (wr->pos < wr->cstr.length) { + size_t chunk_length; + const char *chunk_data = b_cstring_get(wr->cstr, wr->pos, wr->cstr.length - wr->pos, &chunk_length); + size_t to_send = (chunk_length > INT_MAX ? INT_MAX : chunk_length); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&o->connection), (uint8_t *)chunk_data, to_send); + return; + } + + // finish write operation + wr->write_pipe_inst = NULL; + NCDModuleInst_Backend_Up(wr->i); + o->write_inst = NULL; +} + +static void write_pipe_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_pipe_instance *o = vo; + o->i = i; + NCDModuleInst_Backend_PassMemToMethods(i); + + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct process_instance *pinst = params->method_user; + + if (pinst->write_fd == -1) { + ModuleLog(i, BLOG_ERROR, "process did not start successfully, was not opened for writing or a write_pipe was already created"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->connection, BConnection_source_pipe(pinst->write_fd), i->params->iparams->reactor, o, write_pipe_connection_handler)) { + ModuleLog(i, BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection send interface + BConnection_SendAsync_Init(&o->connection); + + // set send done callback + StreamPassInterface_Sender_Init(BConnection_SendAsync_GetIf(&o->connection), write_pipe_send_handler_done, o); + + // set variables + o->state = WRITER_STATE_RUNNING; + o->write_fd = pinst->write_fd; + o->write_inst = NULL; + + // steal write fd from process instance + pinst->write_fd = -1; + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_pipe_func_die (void *vo) +{ + struct write_pipe_instance *o = vo; + + // free connection resources + if (o->state == WRITER_STATE_RUNNING) { + write_pipe_free_connection(o); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static int write_pipe_func_getvar (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct write_pipe_instance *o = vo; + + if (name == NCD_STRING_IS_ERROR) { + int is_error = (o->state == WRITER_STATE_ERROR); + *out = ncd_make_boolean(mem, is_error, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void write_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct write_instance *o = vo; + o->i = i; + + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct write_pipe_instance *write_pipe_inst = params->method_user; + + // check if a write error has already occured + if (write_pipe_inst->state == WRITER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write error has occured"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after a write() has been aborted"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_CLOSED) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed after close() has been called"); + goto fail0; + } + + ASSERT(write_pipe_inst->state == WRITER_STATE_RUNNING) + + // check if there's already a write in progress + if (write_pipe_inst->write_inst) { + ModuleLog(i, BLOG_ERROR, "write() is disallowed while another write() is in progress"); + goto fail0; + } + + // initialize write progress state + o->cstr = NCDVal_StringCstring(data_arg); + o->pos = 0; + + // if there's nothing to send, go up immediately + if (o->cstr.length == 0) { + o->write_pipe_inst = NULL; + NCDModuleInst_Backend_Up(i); + return; + } + + // set write_pipe + o->write_pipe_inst = write_pipe_inst; + + // register write in write_pipe + write_pipe_inst->write_inst = o; + + // start send operation + size_t chunk_length; + const char *chunk_data = b_cstring_get(o->cstr, o->pos, o->cstr.length - o->pos, &chunk_length); + size_t to_send = (chunk_length > INT_MAX ? INT_MAX : chunk_length); + StreamPassInterface_Sender_Send(BConnection_SendAsync_GetIf(&write_pipe_inst->connection), (uint8_t *)chunk_data, to_send); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void write_func_die (void *vo) +{ + struct write_instance *o = vo; + + // if we're sending, abort write_pipe + if (o->write_pipe_inst) { + ASSERT(o->write_pipe_inst->state == WRITER_STATE_RUNNING) + ASSERT(o->write_pipe_inst->write_inst == o) + write_pipe_abort(o->write_pipe_inst); + } + + NCDModuleInst_Backend_Dead(o->i); +} + +static void close_func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct write_pipe_instance *write_pipe_inst = params->method_user; + + // check if a write error has already occured + if (write_pipe_inst->state == WRITER_STATE_ERROR) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write error has occured"); + goto fail0; + } + + // check if the write_pipe has been aborted + if (write_pipe_inst->state == WRITER_STATE_ABORTED) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after a write() has been aborted"); + goto fail0; + } + + // check if the write_pipe has been closed + if (write_pipe_inst->state == WRITER_STATE_CLOSED) { + ModuleLog(i, BLOG_ERROR, "close() is disallowed after close() has been called"); + goto fail0; + } + + // close + write_pipe_close(write_pipe_inst); + + // go up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.start_process", + .func_new2 = process_func_new, + .func_die = process_func_die, + .func_getvar2 = process_func_getvar, + .alloc_size = sizeof(struct process_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::wait", + .func_new2 = wait_func_new, + .func_die = wait_func_die, + .func_getvar2 = wait_func_getvar, + .alloc_size = sizeof(struct wait_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::terminate", + .func_new2 = terminate_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::kill", + .func_new2 = kill_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::read_pipe", + .func_new2 = read_pipe_func_new, + .func_die = read_pipe_func_die, + .func_getvar2 = read_pipe_func_getvar, + .alloc_size = sizeof(struct read_pipe_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::read_pipe::read", + .func_new2 = read_func_new, + .func_die = read_func_die, + .func_getvar2 = read_func_getvar, + .alloc_size = sizeof(struct read_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe", + .func_new2 = write_pipe_func_new, + .func_die = write_pipe_func_die, + .func_getvar2 = write_pipe_func_getvar, + .alloc_size = sizeof(struct write_pipe_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe::write", + .func_new2 = write_func_new, + .func_die = write_func_die, + .alloc_size = sizeof(struct write_instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "sys.start_process::write_pipe::close", + .func_new2 = close_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_start_process = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/sys_watch_directory.c b/external/badvpn_dns/ncd/modules/sys_watch_directory.c new file mode 100644 index 00000000..41f7d427 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_watch_directory.c @@ -0,0 +1,425 @@ +/** + * @file sys_watch_directory.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Directory watcher. + * + * Synopsis: sys.watch_directory(string dir) + * Description: reports directory entry events. Transitions up when an event is detected, and + * goes down waiting for the next event when sys.watch_directory::nextevent() is called. + * The directory is first scanned and "added" events are reported for all files. + * Variables: + * string event_type - what happened with the file: "added", "removed" or "changed" + * string filename - name of the file in the directory the event refers to + * string filepath - "dir/filename" + * + * Synopsis: sys.watch_directory::nextevent() + * Description: makes the watch_directory module transition down in order to report the next event. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define MAX_INOTIFY_EVENTS 128 + +struct instance { + NCDModuleInst *i; + NCDValNullTermString dir_nts; + DIR *dir_handle; + int inotify_fd; + BFileDescriptor bfd; + struct inotify_event events[MAX_INOTIFY_EVENTS]; + int events_count; + int events_index; + int processing; + const char *processing_file; + const char *processing_type; +}; + +static void instance_free (struct instance *o, int is_error); + +static void next_dir_event (struct instance *o) +{ + ASSERT(!o->processing) + ASSERT(o->dir_handle) + + struct dirent *entry; + + do { + // get next entry + errno = 0; + if (!(entry = readdir(o->dir_handle))) { + if (errno != 0) { + ModuleLog(o->i, BLOG_ERROR, "readdir failed"); + instance_free(o, 1); + return; + } + + // close directory + if (closedir(o->dir_handle) < 0) { + ModuleLog(o->i, BLOG_ERROR, "closedir failed"); + o->dir_handle = NULL; + instance_free(o, 1); + return; + } + + // set no dir handle + o->dir_handle = NULL; + + // start receiving inotify events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + return; + } + } while (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")); + + // set event + o->processing_file = entry->d_name; + o->processing_type = "added"; + o->processing = 1; + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void assert_inotify_event (struct instance *o) +{ + ASSERT(o->events_index < o->events_count) + ASSERT(o->events[o->events_index].len % sizeof(o->events[0]) == 0) + ASSERT(o->events[o->events_index].len / sizeof(o->events[0]) <= o->events_count - (o->events_index + 1)) +} + +static const char * translate_inotify_event (struct instance *o) +{ + assert_inotify_event(o); + + struct inotify_event *event = &o->events[o->events_index]; + + if (strlen(event->name) > 0) { + if ((event->mask & (IN_CREATE | IN_MOVED_TO))) { + return "added"; + } + if ((event->mask & (IN_DELETE | IN_MOVED_FROM))) { + return "removed"; + } + if ((event->mask & IN_MODIFY)) { + return "changed"; + } + } + + return NULL; +} + +static void skip_inotify_event (struct instance *o) +{ + assert_inotify_event(o); + + o->events_index += 1 + o->events[o->events_index].len / sizeof(o->events[0]); +} + +static void next_inotify_event (struct instance *o) +{ + ASSERT(!o->processing) + ASSERT(!o->dir_handle) + + // skip any bad events + while (o->events_index < o->events_count && !translate_inotify_event(o)) { + ModuleLog(o->i, BLOG_ERROR, "unknown inotify event"); + skip_inotify_event(o); + } + + if (o->events_index == o->events_count) { + // wait for more events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ); + return; + } + + // set event + o->processing_file = o->events[o->events_index].name; + o->processing_type = translate_inotify_event(o); + o->processing = 1; + + // consume this event + skip_inotify_event(o); + + // signal up + NCDModuleInst_Backend_Up(o->i); +} + +static void inotify_fd_handler (struct instance *o, int events) +{ + if (o->processing) { + ModuleLog(o->i, BLOG_ERROR, "file descriptor error"); + instance_free(o, 1); + return; + } + + ASSERT(!o->dir_handle) + + int res = read(o->inotify_fd, o->events, sizeof(o->events)); + if (res < 0) { + ModuleLog(o->i, BLOG_ERROR, "read failed"); + instance_free(o, 1); + return; + } + + // stop waiting for inotify events + BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, 0); + + ASSERT(res <= sizeof(o->events)) + ASSERT(res % sizeof(o->events[0]) == 0) + + // setup buffer state + o->events_count = res / sizeof(o->events[0]); + o->events_index = 0; + + // process inotify events + next_inotify_event(o); +} + +static void next_event (struct instance *o) +{ + ASSERT(o->processing) + + // set not processing + o->processing = 0; + + // signal down + NCDModuleInst_Backend_Down(o->i); + + if (o->dir_handle) { + next_dir_event(o); + return; + } else { + next_inotify_event(o); + return; + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef dir_arg; + if (!NCDVal_ListRead(params->args, 1, &dir_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsStringNoNulls(dir_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // null terminate dir + if (!NCDVal_StringNullTerminate(dir_arg, &o->dir_nts)) { + ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed"); + goto fail0; + } + + // open inotify + if ((o->inotify_fd = inotify_init()) < 0) { + ModuleLog(o->i, BLOG_ERROR, "inotify_init failed"); + goto fail1; + } + + // add watch + if (inotify_add_watch(o->inotify_fd, o->dir_nts.data, IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) { + ModuleLog(o->i, BLOG_ERROR, "inotify_add_watch failed"); + goto fail2; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->inotify_fd)) { + ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->inotify_fd, (BFileDescriptor_handler)inotify_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->i->params->iparams->reactor, &o->bfd)) { + ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + + // open directory + if (!(o->dir_handle = opendir(o->dir_nts.data))) { + ModuleLog(o->i, BLOG_ERROR, "opendir failed"); + goto fail3; + } + + // set not processing + o->processing = 0; + + // read first directory entry + next_dir_event(o); + return; + +fail3: + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); +fail2: + if (close(o->inotify_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } +fail1: + NCDValNullTermString_Free(&o->dir_nts); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +void instance_free (struct instance *o, int is_error) +{ + // close directory + if (o->dir_handle) { + if (closedir(o->dir_handle) < 0) { + ModuleLog(o->i, BLOG_ERROR, "closedir failed"); + } + } + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd); + + // close inotify + if (close(o->inotify_fd) < 0) { + ModuleLog(o->i, BLOG_ERROR, "close failed"); + } + + // free dir nts + NCDValNullTermString_Free(&o->dir_nts); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + instance_free(o, 0); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->processing) + + if (!strcmp(name, "event_type")) { + *out = NCDVal_NewString(mem, o->processing_type); + return 1; + } + + if (!strcmp(name, "filename")) { + *out = NCDVal_NewString(mem, o->processing_file); + return 1; + } + + if (!strcmp(name, "filepath")) { + char *str = concat_strings(3, o->dir_nts.data, "/", o->processing_file); + if (!str) { + ModuleLog(o->i, BLOG_ERROR, "concat_strings failed"); + goto fail; + } + + *out = NCDVal_NewString(mem, str); + + free(str); + return 1; + } + + return 0; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!mo->processing) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_directory", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_directory::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_directory = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/sys_watch_input.c b/external/badvpn_dns/ncd/modules/sys_watch_input.c new file mode 100644 index 00000000..6039bee6 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_watch_input.c @@ -0,0 +1,455 @@ +/** + * @file sys_watch_input.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Input device watcher. + * + * Synopsis: sys.watch_input(string devnode_type) + * Description: reports input device events. Transitions up when an event is detected, and + * goes down waiting for the next event when sys.watch_input::nextevent() is called. + * On startup, "added" events are reported for existing input devices. + * Arguments: + * string devnode_type - device node type, for example "event", "mouse" or "js". + * Variables: + * string event_type - what happened with the input device: "added" or "removed" + * string devname - device node path + * string device_type - input device type, "tablet", "joystick", "touchscreen", "mouse", + * "touchpad", "key", "keyboard" or "unknown" + * + * Synopsis: sys.watch_input::nextevent() + * Description: makes the watch_input module transition down in order to report the next event. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct device { + char *devname; + char *devpath; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + const char *devnode_type; + size_t devnode_type_len; + NCDUdevClient client; + LinkedList1 devices_list; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_devname (struct instance *o, const char *devname) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devname, devname)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); + while (list_node) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + list_node = LinkedList1Node_Next(list_node); + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free devname + free(device->devname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *devname, const char *device_type, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set devname + if (!BStringMap_Set(&map, "devname", devname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set device type + if (!BStringMap_Set(&map, "device_type", device_type)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static int devname_is_type (const char *devname, const char *devname_type, size_t devname_type_len) +{ + // skip digits at the end + size_t i; + for (i = strlen(devname); i > 0; i--) { + if (!isdigit(devname[i - 1])) { + break; + } + } + + // check if devname_type precedes skipped digits + for (size_t j = devname_type_len; j > 0; j--) { + if (!(i > 0 && devname[i - 1] == devname_type[j - 1])) { + return 0; + } + i--; + } + + // check if slash precedes devname_type + if (!(i > 0 && devname[i - 1] == '/')) { + return 0; + } + + return 1; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *devname, const char *devpath, const char *device_type) +{ + ASSERT(!find_device_by_devname(o, devname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init devname + if (!(device->devname = strdup(devname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // init removed map + if (!make_event_map(o, 0, devname, device_type, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, devname, device_type, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->devname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", devname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM"); + const char *devname = BStringMap_Get(cache_map, "DEVNAME"); + + if (!(subsystem && !strcmp(subsystem, "input") && devname && devname_is_type(devname, o->devnode_type, o->devnode_type_len))) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && strcmp(ex_device->devname, devname)) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_devname_device = find_device_by_devname(o, devname); + if (ex_devname_device) { + remove_device(o, ex_devname_device); + } + + // determine type + const char *device_type = "unknown"; + if (BStringMap_Get(cache_map, "ID_INPUT_TABLET")) { + device_type = "tablet"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_JOYSTICK")) { + device_type = "joystick"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHSCREEN")) { + device_type = "touchscreen"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_MOUSE")) { + device_type = "mouse"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_TOUCHPAD")) { + device_type = "touchpad"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_KEY")) { + device_type = "key"; + } + else if (BStringMap_Get(cache_map, "ID_INPUT_KEYBOARD")) { + device_type = "keyboard"; + } + + add_device(o, devname, devpath, device_type); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef devnode_type_arg; + if (!NCDVal_ListRead(params->args, 1, &devnode_type_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(devnode_type_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + o->devnode_type = NCDVal_StringData(devnode_type_arg); + o->devnode_type_len = NCDVal_StringLength(devnode_type_arg); + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->devices_list)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + free_device(o, device, 1); + } + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_input", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_input::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_input = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/sys_watch_usb.c b/external/badvpn_dns/ncd/modules/sys_watch_usb.c new file mode 100644 index 00000000..d2e6b1c1 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/sys_watch_usb.c @@ -0,0 +1,421 @@ +/** + * @file sys_watch_usb.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * USB device watcher. + * + * Synopsis: sys.watch_usb() + * Description: reports USB device events. Transitions up when an event is detected, and + * goes down waiting for the next event when ->nextevent() is called. + * On startup, "added" events are reported for existing USB devices. + * + * Variables: + * string event_type - what happened with the USB device: "added" or "removed" + * string devname - device node path, e.g. /dev/bus/usb/XXX/YYY + * string vendor_id - vendor ID, e.g. 046d + * string model_id - model ID, e.g. c03e + * + * Synopsis: sys.watch_usb::nextevent() + * Description: makes the watch_usb module transition down in order to report the next event. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct device { + char *devname; + char *devpath; + uint16_t vendor_id; + uint16_t model_id; + BStringMap removed_map; + LinkedList1Node devices_list_node; +}; + +struct instance { + NCDModuleInst *i; + NCDUdevClient client; + LinkedList1 devices_list; + event_template templ; +}; + +static void templ_func_free (struct instance *o, int is_error); + +static struct device * find_device_by_devname (struct instance *o, const char *devname) +{ + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devname, devname)) { + return device; + } + } + + return NULL; +} + +static struct device * find_device_by_devpath (struct instance *o, const char *devpath) +{ + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node); + if (!strcmp(device->devpath, devpath)) { + return device; + } + } + + return NULL; +} + +static void free_device (struct instance *o, struct device *device, int have_removed_map) +{ + // remove from devices list + LinkedList1_Remove(&o->devices_list, &device->devices_list_node); + + // free removed map + if (have_removed_map) { + BStringMap_Free(&device->removed_map); + } + + // free devpath + free(device->devpath); + + // free devname + free(device->devname); + + // free structure + free(device); +} + +static int make_event_map (struct instance *o, int added, const char *devname, uint16_t vendor_id, uint16_t model_id, BStringMap *out_map) +{ + // init map + BStringMap map; + BStringMap_Init(&map); + + // set type + if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set devname + if (!BStringMap_Set(&map, "devname", devname)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set vendor ID + char vendor_id_str[5]; + sprintf(vendor_id_str, "%04"PRIx16, vendor_id); + if (!BStringMap_Set(&map, "vendor_id", vendor_id_str)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + // set model ID + char model_id_str[5]; + sprintf(model_id_str, "%04"PRIx16, model_id); + if (!BStringMap_Set(&map, "model_id", model_id_str)) { + ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + + *out_map = map; + return 1; + +fail1: + BStringMap_Free(&map); + return 0; +} + +static void queue_event (struct instance *o, BStringMap map) +{ + // pass event to template + int was_empty; + event_template_queue(&o->templ, map, &was_empty); + + // if event queue was empty, stop receiving udev events + if (was_empty) { + NCDUdevClient_Pause(&o->client); + } +} + +static void add_device (struct instance *o, const char *devname, const char *devpath, uint16_t vendor_id, uint16_t model_id) +{ + ASSERT(!find_device_by_devname(o, devname)) + ASSERT(!find_device_by_devpath(o, devpath)) + + // allocate structure + struct device *device = malloc(sizeof(*device)); + if (!device) { + ModuleLog(o->i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init devname + if (!(device->devname = strdup(devname))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // init devpath + if (!(device->devpath = strdup(devpath))) { + ModuleLog(o->i, BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // set vendor and model ID + device->vendor_id = vendor_id; + device->model_id = model_id; + + // init removed map + if (!make_event_map(o, 0, devname, vendor_id, model_id, &device->removed_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail3; + } + + // init added map + BStringMap added_map; + if (!make_event_map(o, 1, devname, vendor_id, model_id, &added_map)) { + ModuleLog(o->i, BLOG_ERROR, "make_event_map failed"); + goto fail4; + } + + // insert to devices list + LinkedList1_Append(&o->devices_list, &device->devices_list_node); + + // queue event + queue_event(o, added_map); + return; + +fail4: + BStringMap_Free(&device->removed_map); +fail3: + free(device->devpath); +fail2: + free(device->devname); +fail1: + free(device); +fail0: + ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", devname); +} + +static void remove_device (struct instance *o, struct device *device) +{ + queue_event(o, device->removed_map); + free_device(o, device, 0); +} + +static void next_event (struct instance *o) +{ + ASSERT(event_template_is_enabled(&o->templ)) + + // order template to finish the current event + int is_empty; + event_template_dequeue(&o->templ, &is_empty); + + // if template has no events, continue udev events + if (is_empty) { + NCDUdevClient_Continue(&o->client); + } +} + +static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map) +{ + // lookup existing device with this devpath + struct device *ex_device = find_device_by_devpath(o, devpath); + // lookup cache entry + const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath); + + if (!cache_map) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + const char *subsystem = BStringMap_Get(cache_map, "SUBSYSTEM"); + const char *devname = BStringMap_Get(cache_map, "DEVNAME"); + const char *devtype = BStringMap_Get(cache_map, "DEVTYPE"); + const char *vendor_id_str = BStringMap_Get(cache_map, "ID_VENDOR_ID"); + const char *model_id_str = BStringMap_Get(cache_map, "ID_MODEL_ID"); + + uintmax_t vendor_id; + uintmax_t model_id; + + if (!(subsystem && !strcmp(subsystem, "usb") && + devname && + devtype && !strcmp(devtype, "usb_device") && + vendor_id_str && parse_unsigned_hex_integer(vendor_id_str, &vendor_id) && + model_id_str && parse_unsigned_hex_integer(model_id_str, &model_id) + )) { + if (ex_device) { + remove_device(o, ex_device); + } + goto out; + } + + if (ex_device && ( + strcmp(ex_device->devname, devname) || + ex_device->vendor_id != vendor_id || ex_device->model_id != model_id + )) { + remove_device(o, ex_device); + ex_device = NULL; + } + + if (!ex_device) { + struct device *ex_devname_device = find_device_by_devname(o, devname); + if (ex_devname_device) { + remove_device(o, ex_devname_device); + } + + add_device(o, devname, devpath, vendor_id, model_id); + } + +out: + free(devpath); + if (have_map) { + BStringMap_Free(&map); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init client + NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler); + + // init devices list + LinkedList1_Init(&o->devices_list); + + event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void templ_func_free (struct instance *o, int is_error) +{ + // free devices + while (!LinkedList1_IsEmpty(&o->devices_list)) { + struct device *device = UPPER_OBJECT(LinkedList1_GetFirst(&o->devices_list), struct device, devices_list_node); + free_device(o, device, 1); + } + + // free client + NCDUdevClient_Free(&o->client); + + if (is_error) { + NCDModuleInst_Backend_DeadError(o->i); + } else { + NCDModuleInst_Backend_Dead(o->i); + } +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + event_template_die(&o->templ); +} + +static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + return event_template_getvar(&o->templ, name, mem, out); +} + +static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // make sure we are currently reporting an event + if (!event_template_is_enabled(&mo->templ)) { + ModuleLog(i, BLOG_ERROR, "not reporting an event"); + goto fail0; + } + + // signal up. + // Do it before finishing the event so our process does not advance any further if + // we would be killed the event provider going down. + NCDModuleInst_Backend_Up(i); + + // wait for next event + next_event(mo); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "sys.watch_usb", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar = func_getvar, + .alloc_size = sizeof(struct instance) + }, { + .type = "sys.watch_usb::nextevent", + .func_new2 = nextevent_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_sys_watch_usb = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/timer.c b/external/badvpn_dns/ncd/modules/timer.c new file mode 100644 index 00000000..6a748f99 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/timer.c @@ -0,0 +1,146 @@ +/** + * @file timer.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * periodic_timer(string down_time, string up_time) + * + * Description: + * Periodically goes up and down. Starting in the down state, waits 'down_time' + * milliseconds. Then it goes up, and after 'up_time' milliseconds, goes back down, + * and the process repeats. When requested to terminate, terminates immedietely. + */ + +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +#define STATE_DOWN 1 +#define STATE_UP 2 + +struct instance { + NCDModuleInst *i; + btime_t down_time; + btime_t up_time; + BTimer timer; + int state; +}; + +static void timer_handler (void *vo) +{ + struct instance *o = vo; + + switch (o->state) { + case STATE_DOWN: { + o->state = STATE_UP; + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->up_time); + NCDModuleInst_Backend_Up(o->i); + } break; + + case STATE_UP: { + o->state = STATE_DOWN; + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->down_time); + NCDModuleInst_Backend_Down(o->i); + } break; + + default: ASSERT(0); + } +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef down_time_arg; + NCDValRef up_time_arg; + if (!NCDVal_ListRead(params->args, 2, &down_time_arg, &up_time_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(down_time_arg) || !NCDVal_IsString(up_time_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // read times + if (!ncd_read_time(down_time_arg, &o->down_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong down time"); + goto fail0; + } + if (!ncd_read_time(up_time_arg, &o->up_time)) { + ModuleLog(o->i, BLOG_ERROR, "wrong up time"); + goto fail0; + } + + // init timer + BTimer_Init(&o->timer, 0, timer_handler, o); + + // set state down + o->state = STATE_DOWN; + + // set timer + BReactor_SetTimerAfter(o->i->params->iparams->reactor, &o->timer, o->down_time); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free timer + BReactor_RemoveTimer(o->i->params->iparams->reactor, &o->timer); + + NCDModuleInst_Backend_Dead(o->i); +} + +static struct NCDModule modules[] = { + { + .type = "periodic_timer", + .func_new2 = func_new, + .func_die = func_die, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_timer = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/to_string.c b/external/badvpn_dns/ncd/modules/to_string.c new file mode 100644 index 00000000..2380c34f --- /dev/null +++ b/external/badvpn_dns/ncd/modules/to_string.c @@ -0,0 +1,116 @@ +/** + * @file to_string.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * to_string(value) + * Variables: + * string (empty) - value, converted to string + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + char *str; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // convert to string + if (!(o->str = NCDValGenerator_Generate(value_arg))) { + ModuleLog(i, BLOG_ERROR, "NCDValGenerator_Generate failed"); + goto fail0; + } + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free string + free(o->str); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewString(mem, o->str); + return 1; + } + + return 0; +} + +static struct NCDModule modules[] = { + { + .type = "to_string", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_to_string = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/try.c b/external/badvpn_dns/ncd/modules/try.c new file mode 100644 index 00000000..4c4613c5 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/try.c @@ -0,0 +1,302 @@ +/** + * @file try.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * try(string template_name, list args) + * + * Description: + * Does the following: + * 1. Starts a template process from the specified template and arguments. + * 2. Waits for the process to initialize completely, or for a _try->assert() + * assertion to fail. + * 3. Initiates termination of the process and waits for it to terminate. + * 4. Goes to up state. The "succeeded" variable reflects whether the process + * managed to initialize, or an assertion failed. + * If at any point during these steps termination of the try statement is + * requested, requests the process to terminate (if not already), and dies + * when it terminates. + * + * Variables: + * string succeeded - "true" if the template process finished, "false" if assert + * was called. + * + * Synopsis: + * try.try::assert(string cond) + * + * Description: + * Call as _try->assert() from the template process. If cond is "true", + * does nothing. Else, initiates termination of the process (if not already), + * and marks the try operation as not succeeded. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +struct instance { + NCDModuleInst *i; + NCDModuleProcess process; + int state; + int dying; + int succeeded; +}; + +#define STATE_INIT 1 +#define STATE_DEINIT 2 +#define STATE_FINISHED 3 + +static void process_handler_event (NCDModuleProcess *process, int event); +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object); +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object); +static void start_terminating (struct instance *o); +static void instance_free (struct instance *o); + +enum {STRING_TRY, STRING_TRY_TRY}; + +static const char *strings[] = { + "_try", "try.try", NULL +}; + +static void process_handler_event (NCDModuleProcess *process, int event) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + + switch (event) { + case NCDMODULEPROCESS_EVENT_UP: { + ASSERT(o->state == STATE_INIT) + + // start terminating + start_terminating(o); + } break; + + case NCDMODULEPROCESS_EVENT_DOWN: { + ASSERT(o->state == STATE_INIT) + + // continue + NCDModuleProcess_Continue(&o->process); + } break; + + case NCDMODULEPROCESS_EVENT_TERMINATED: { + ASSERT(o->state == STATE_DEINIT) + + // free process + NCDModuleProcess_Free(&o->process); + + // die finally if requested + if (o->dying) { + instance_free(o); + return; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + + // set state finished + o->state = STATE_FINISHED; + } break; + } +} + +static int process_func_getspecialobj (NCDModuleProcess *process, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = UPPER_OBJECT(process, struct instance, process); + ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT) + + if (name == NCD_STRING_CALLER) { + *out_object = NCDObject_Build(-1, o, NCDObject_no_getvar, process_caller_object_func_getobj); + return 1; + } + + if (name == ModuleString(o->i, STRING_TRY)) { + *out_object = NCDObject_Build(ModuleString(o->i, STRING_TRY_TRY), o, NCDObject_no_getvar, NCDObject_no_getobj); + return 1; + } + + return 0; +} + +static int process_caller_object_func_getobj (const NCDObject *obj, NCD_string_id_t name, NCDObject *out_object) +{ + struct instance *o = NCDObject_DataPtr(obj); + ASSERT(o->state == STATE_INIT || o->state == STATE_DEINIT) + + return NCDModuleInst_Backend_GetObj(o->i, name, out_object); +} + +static void start_terminating (struct instance *o) +{ + ASSERT(o->state == STATE_INIT) + + // request process termination + NCDModuleProcess_Terminate(&o->process); + + // set state deinit + o->state = STATE_DEINIT; +} + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // check arguments + NCDValRef template_name_arg; + NCDValRef args_arg; + if (!NCDVal_ListRead(params->args, 2, &template_name_arg, &args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(template_name_arg) || !NCDVal_IsList(args_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + // start process + if (!NCDModuleProcess_InitValue(&o->process, i, template_name_arg, args_arg, process_handler_event)) { + ModuleLog(o->i, BLOG_ERROR, "NCDModuleProcess_Init failed"); + goto fail0; + } + + // set special object function + NCDModuleProcess_SetSpecialFuncs(&o->process, process_func_getspecialobj); + + // set state init, not dying, assume succeeded + o->state = STATE_INIT; + o->dying = 0; + o->succeeded = 1; + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void instance_free (struct instance *o) +{ + NCDModuleInst_Backend_Dead(o->i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + ASSERT(!o->dying) + + // if we're finished, die immediately + if (o->state == STATE_FINISHED) { + instance_free(o); + return; + } + + // set dying + o->dying = 1; + + // start terminating if not already + if (o->state == STATE_INIT) { + start_terminating(o); + } +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + ASSERT(o->state == STATE_FINISHED) + ASSERT(!o->dying) + + if (name == NCD_STRING_SUCCEEDED) { + *out = ncd_make_boolean(mem, o->succeeded, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void assert_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // check arguments + NCDValRef cond_arg; + if (!NCDVal_ListRead(params->args, 1, &cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail1; + } + if (!NCDVal_IsString(cond_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail1; + } + + // get instance + struct instance *mo = params->method_user; + ASSERT(mo->state == STATE_INIT || mo->state == STATE_DEINIT) + + // signal up + NCDModuleInst_Backend_Up(i); + + if (!NCDVal_StringEquals(cond_arg, "true")) { + // mark not succeeded + mo->succeeded = 0; + + // start terminating if not already + if (mo->state == STATE_INIT) { + start_terminating(mo); + } + } + + return; + +fail1: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "try", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "try.try::assert", + .func_new2 = assert_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_try = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/value.c b/external/badvpn_dns/ncd/modules/value.c new file mode 100644 index 00000000..41ccf350 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/value.c @@ -0,0 +1,2102 @@ +/** + * @file value.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Synopsis: + * value(value) + * value value::get(where) + * value value::try_get(where) + * value value::getpath(list path) + * value value::insert(where, what) + * value value::insert(what) + * value value::replace(where, what) + * value value::replace_this(value) + * value value::insert_undo(where, what) + * value value::insert_undo(what) + * value value::replace_undo(where, what) + * value value::replace_this_undo(value) + * + * Description: + * Value objects allow examining and manipulating values. + * These value objects are actually references to internal value structures, which + * may be shared between value objects. + * + * value(value) constructs a new value object from the given value. + * + * value::get(where) constructs a value object for the element at position 'where' + * (for a list), or the value corresponding to key 'where' (for a map). It is an + * error if the base value is not a list or a map, the index is out of bounds of + * the list, or the key does not exist in the map. + * The resulting value object is NOT a copy, and shares (part of) the same + * underlying value structure as the base value object. Deleting it will remove + * it from the list or map it is part of. + * + * value::try_get(where) is like get(), except that if any restriction on 'where' + * is violated, no error is triggered; instead, the value object is constructed + * as being deleted; this state is exposed via the 'exists' variable. + * This can be used to check for the presence of a key in a map, and in case it + * exists, allow access to the corresponding value without another get() statement. + * + * value::getpath(path) is like get(), except that it performs multiple + * consecutive resolutions. Also, if the path is an empty list, it performs + * no resulution at all. + * + * value::insert(where, what) constructs a value object by inserting into an + * existing value object. + * For lists, 'where' is the index of the element to insert before, or the length + * of the list to append to it. + * For maps, 'where' is the key to insert under. If the key already exists in the + * map, its value is replaced; any references to the old value however remain valid. + * + * value::insert(what) constructs a value object by appending to a list. An error + * is triggered if the base value is not a list. Assuming 'list' is a list value, + * list->insert(X) is equivalent to list->insert(list.length, X). + * + * value::replace(where, what) is like insert(), exept that, when inserting into a + * list, the value at the specified index is replaced with the new value (unless + * the index is equal to the length of the list). + * + * insert_undo() and replace_undo() are versions of insert() and replace() which + * attempt to revert the modifications when they deinitialize. + * Specifically, they work like that: + * - On initiialization, they take an internal reference to the value being replaced + * (if any; note that insert_undo() into a list never replaces a value). + * - On deinitialization, they remove the the inserted value from its parent (if there + * is one), and insert the old replaced value (to which a reference was kept) in that + * place (if any, and assuming it has not been deleted). + * Note that if the inserted value changes parents in between init and deinit, the + * result of undoing may be unexpected. + * + * Variables: + * (empty) - the value stored in the value object + * type - type of the value; "string", "list" or "map" + * length - number of elements in the list or map, or the number of bytes in a + * string + * keys - a list of keys in the map (only if the value is a map) + * exists - "true" or "false", reflecting whether the value object holds a value + * (is not in deleted state) + * + * Synopsis: + * value::remove(where) + * value::delete() + * + * Description: + * value::remove(where) removes from an existing value object. + * For lists, 'where' is the index of the element to remove, and must be in range. + * For maps, 'where' is the key to remove, and must be an existing key. + * In any case, any references to the removed value remain valid. + * + * value::delete() deletes the underlying value data of this value object. + * After delection, the value object enters a deleted state, which will cause any + * operation on it to fail. Any other value objects which referred to the same value + * or parts of it will too enter deleted state. If the value was an element + * in a list or map, is is removed from it. + * + * Synopsis: + * value value::substr(string start [, string length]) + * + * Description: + * Constructs a string value by extracting a part of a string. + * 'start' specifies the index of the character (from zero) where the substring to + * extract starts, and must be <= the length of the string. + * 'length' specifies the maximum number of characters to extract, if given. + * The newly constructed value is a copy of the extracted substring. + * The value must be a string value. + * + * Synopsis: + * value::reset(what) + * + * Description: + * Effectively deconstructs and reconstructs the value object. More precisely, + * it builds a new value structure from 'what', possibly invokes a scheduled undo + * operation (as scheduled by insert_undo() and replace_undo()), sets up this + * value object to reference the newly built value structure, without any scheduled + * undo operation. + * + * Synopsis: + * value::append(append_val) + * + * Description: + * Only defined when the existing value object is a string or list. If it is a string, + * appends the string 'append_val' to this string value; 'append_val' must be a string. + * If is is a list, inserts 'append_val' to the end of this list value. Unlike insert(), + * the resulting append() object is not itself a value object (which in case of insert() + * serves as a reference to the new value). + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) +#define ModuleString(i, id) ((i)->m->group->strings[(id)]) + +#define STOREDSTRING_TYPE (NCDVAL_STRING | (0 << 3)) +#define IDSTRING_TYPE (NCDVAL_STRING | (1 << 3)) +#define EXTERNALSTRING_TYPE (NCDVAL_STRING | (2 << 3)) +#define COMPOSEDSTRING_TYPE (NCDVAL_STRING | (3 << 3)) + +struct value; + +#include "value_maptree.h" +#include + +struct valref { + struct value *v; + LinkedList0Node refs_list_node; +}; + +typedef void (*value_deinit_func) (void *deinit_data, NCDModuleInst *i); + +struct instance { + NCDModuleInst *i; + struct valref ref; + value_deinit_func deinit_func; + void *deinit_data; +}; + +struct value { + LinkedList0 refs_list; + + struct value *parent; + union { + struct { + IndexedListNode list_contents_il_node; + } list_parent; + struct { + NCDValMem key_mem; + NCDValRef key; + MapTreeNode maptree_node; + } map_parent; + }; + + int type; + union { + struct { + char *data; + size_t length; + } storedstring; + struct { + NCD_string_id_t id; + NCDStringIndex *string_index; + } idstring; + struct { + const char *data; + size_t length; + BRefTarget *ref_target; + } externalstring; + struct { + NCDValComposedStringResource resource; + size_t offset; + size_t length; + } composedstring; + struct { + IndexedList list_contents_il; + } list; + struct { + MapTree map_tree; + } map; + }; +}; + +static const char * get_type_str (int type); +static void value_cleanup (struct value *v); +static void value_delete (struct value *v); +static struct value * value_init_storedstring (NCDModuleInst *i, const char *str, size_t len); +static struct value * value_init_idstring (NCDModuleInst *i, NCD_string_id_t id, NCDStringIndex *string_index); +static struct value * value_init_externalstring (NCDModuleInst *i, const char *data, size_t length, BRefTarget *ref_target); +static struct value * value_init_composedstring (NCDModuleInst *i, NCDValComposedStringResource resource, size_t offset, size_t length); +static int value_is_string (struct value *v); +static size_t value_string_length (struct value *v); +static void value_string_copy_out (struct value *v, char *dest); +static void value_string_set_allocd (struct value *v, char *data, size_t length); +static struct value * value_init_list (NCDModuleInst *i); +static size_t value_list_len (struct value *v); +static struct value * value_list_at (struct value *v, size_t index); +static size_t value_list_indexof (struct value *v, struct value *ev); +static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index); +static void value_list_remove (struct value *list, struct value *v); +static struct value * value_init_map (NCDModuleInst *i); +static size_t value_map_len (struct value *map); +static struct value * value_map_at (struct value *map, size_t index); +static struct value * value_map_find (struct value *map, NCDValRef key); +static int value_map_insert (struct value *map, struct value *v, NCDValMem mem, NCDValSafeRef key, NCDModuleInst *i); +static void value_map_remove (struct value *map, struct value *v); +static void value_map_remove2 (struct value *map, struct value *v, NCDValMem *out_mem, NCDValSafeRef *out_key); +static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValRef value); +static int value_to_value (NCDModuleInst *i, struct value *v, NCDValMem *mem, NCDValRef *out_value); +static struct value * value_get (NCDModuleInst *i, struct value *v, NCDValRef where, int no_error); +static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValRef path); +static struct value * value_insert (NCDModuleInst *i, struct value *v, NCDValRef where, NCDValRef what, int is_replace, struct value **out_oldv); +static struct value * value_insert_simple (NCDModuleInst *i, struct value *v, NCDValRef what); +static int value_remove (NCDModuleInst *i, struct value *v, NCDValRef where); +static int value_append (NCDModuleInst *i, struct value *v, NCDValRef data); +static void valref_init (struct valref *r, struct value *v); +static void valref_free (struct valref *r); +static struct value * valref_val (struct valref *r); +static void valref_break (struct valref *r); + +enum {STRING_EXISTS, STRING_KEYS}; + +static const char *strings[] = { + "exists", "keys", NULL +}; + +#include "value_maptree.h" +#include + +static const char * get_type_str (int type) +{ + switch (type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: + return "string"; + case NCDVAL_LIST: + return "list"; + case NCDVAL_MAP: + return "map"; + } + ASSERT(0) + return NULL; +} + +static void value_cleanup (struct value *v) +{ + if (v->parent || !LinkedList0_IsEmpty(&v->refs_list)) { + return; + } + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + case NCDVAL_LIST: { + while (value_list_len(v) > 0) { + struct value *ev = value_list_at(v, 0); + value_list_remove(v, ev); + value_cleanup(ev); + } + } break; + + case NCDVAL_MAP: { + while (value_map_len(v) > 0) { + struct value *ev = value_map_at(v, 0); + value_map_remove(v, ev); + value_cleanup(ev); + } + } break; + + default: ASSERT(0); + } + + free(v); +} + +static void value_delete (struct value *v) +{ + if (v->parent) { + switch (v->parent->type) { + case NCDVAL_LIST: { + value_list_remove(v->parent, v); + } break; + case NCDVAL_MAP: { + value_map_remove(v->parent, v); + } break; + default: ASSERT(0); + } + } + + LinkedList0Node *ln; + while (ln = LinkedList0_GetFirst(&v->refs_list)) { + struct valref *r = UPPER_OBJECT(ln, struct valref, refs_list_node); + ASSERT(r->v == v) + valref_break(r); + } + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + case NCDVAL_LIST: { + while (value_list_len(v) > 0) { + struct value *ev = value_list_at(v, 0); + value_delete(ev); + } + } break; + + case NCDVAL_MAP: { + while (value_map_len(v) > 0) { + struct value *ev = value_map_at(v, 0); + value_delete(ev); + } + } break; + + default: ASSERT(0); + } + + free(v); +} + +static struct value * value_init_storedstring (NCDModuleInst *i, const char *str, size_t len) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = STOREDSTRING_TYPE; + + if (!(v->storedstring.data = BAlloc(len))) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + goto fail1; + } + + if (str) { + memcpy(v->storedstring.data, str, len); + } + + v->storedstring.length = len; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static struct value * value_init_idstring (NCDModuleInst *i, NCD_string_id_t id, NCDStringIndex *string_index) +{ + ASSERT(string_index == i->params->iparams->string_index) + + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = IDSTRING_TYPE; + + v->idstring.id = id; + v->idstring.string_index = string_index; + + return v; + +fail0: + return NULL; +} + +static struct value * value_init_externalstring (NCDModuleInst *i, const char *data, size_t length, BRefTarget *ref_target) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (ref_target) { + if (!BRefTarget_Ref(ref_target)) { + ModuleLog(i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail1; + } + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = EXTERNALSTRING_TYPE; + + v->externalstring.data = data; + v->externalstring.length = length; + v->externalstring.ref_target = ref_target; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static struct value * value_init_composedstring (NCDModuleInst *i, NCDValComposedStringResource resource, size_t offset, size_t length) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (resource.ref_target) { + if (!BRefTarget_Ref(resource.ref_target)) { + ModuleLog(i, BLOG_ERROR, "BRefTarget_Ref failed"); + goto fail1; + } + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = COMPOSEDSTRING_TYPE; + + v->composedstring.resource = resource; + v->composedstring.offset = offset; + v->composedstring.length = length; + + return v; + +fail1: + free(v); +fail0: + return NULL; +} + +static int value_is_string (struct value *v) +{ + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: + return 1; + default: + return 0; + } +} + +static size_t value_string_length (struct value *v) +{ + ASSERT(value_is_string(v)) + + switch (v->type) { + case STOREDSTRING_TYPE: + return v->storedstring.length; + case IDSTRING_TYPE: + return NCDStringIndex_Length(v->idstring.string_index, v->idstring.id); + case EXTERNALSTRING_TYPE: + return v->externalstring.length; + case COMPOSEDSTRING_TYPE: + return v->composedstring.length; + default: + ASSERT(0); + return 0; + } +} + +static void value_string_copy_out (struct value *v, char *dest) +{ + ASSERT(value_is_string(v)) + + switch (v->type) { + case STOREDSTRING_TYPE: + memcpy(dest, v->storedstring.data, v->storedstring.length); + break; + case IDSTRING_TYPE: + memcpy(dest, NCDStringIndex_Value(v->idstring.string_index, v->idstring.id), NCDStringIndex_Length(v->idstring.string_index, v->idstring.id)); + break; + case EXTERNALSTRING_TYPE: + memcpy(dest, v->externalstring.data, v->externalstring.length); + break; + case COMPOSEDSTRING_TYPE: { + b_cstring cstr = NCDValComposedStringResource_Cstring(v->composedstring.resource, v->composedstring.offset, v->composedstring.length); + b_cstring_copy_to_buf(cstr, 0, cstr.length, dest); + } break; + default: + ASSERT(0); + } +} + +static void value_string_set_allocd (struct value *v, char *data, size_t length) +{ + ASSERT(value_is_string(v)) + ASSERT(data) + + switch (v->type) { + case STOREDSTRING_TYPE: { + BFree(v->storedstring.data); + } break; + + case IDSTRING_TYPE: { + } break; + + case EXTERNALSTRING_TYPE: { + if (v->externalstring.ref_target) { + BRefTarget_Deref(v->externalstring.ref_target); + } + } break; + + case COMPOSEDSTRING_TYPE: { + if (v->composedstring.resource.ref_target) { + BRefTarget_Deref(v->composedstring.resource.ref_target); + } + } break; + + default: + ASSERT(0); + } + + v->type = STOREDSTRING_TYPE; + v->storedstring.data = data; + v->storedstring.length = length; +} + +static struct value * value_init_list (NCDModuleInst *i) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + return NULL; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = NCDVAL_LIST; + + IndexedList_Init(&v->list.list_contents_il); + + return v; +} + +static size_t value_list_len (struct value *v) +{ + ASSERT(v->type == NCDVAL_LIST) + + return IndexedList_Count(&v->list.list_contents_il); +} + +static struct value * value_list_at (struct value *v, size_t index) +{ + ASSERT(v->type == NCDVAL_LIST) + ASSERT(index < value_list_len(v)) + + IndexedListNode *iln = IndexedList_GetAt(&v->list.list_contents_il, index); + ASSERT(iln) + + struct value *e = UPPER_OBJECT(iln, struct value, list_parent.list_contents_il_node); + ASSERT(e->parent == v) + + return e; +} + +static size_t value_list_indexof (struct value *v, struct value *ev) +{ + ASSERT(v->type == NCDVAL_LIST) + ASSERT(ev->parent == v) + + uint64_t index = IndexedList_IndexOf(&v->list.list_contents_il, &ev->list_parent.list_contents_il_node); + ASSERT(index < value_list_len(v)) + + return index; +} + +static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index) +{ + ASSERT(list->type == NCDVAL_LIST) + ASSERT(!v->parent) + ASSERT(index <= value_list_len(list)) + + if (value_list_len(list) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "list has too many elements"); + return 0; + } + + IndexedList_InsertAt(&list->list.list_contents_il, &v->list_parent.list_contents_il_node, index); + v->parent = list; + + return 1; +} + +static void value_list_remove (struct value *list, struct value *v) +{ + ASSERT(list->type == NCDVAL_LIST) + ASSERT(v->parent == list) + + IndexedList_Remove(&list->list.list_contents_il, &v->list_parent.list_contents_il_node); + v->parent = NULL; +} + +static struct value * value_init_map (NCDModuleInst *i) +{ + struct value *v = malloc(sizeof(*v)); + if (!v) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + return NULL; + } + + LinkedList0_Init(&v->refs_list); + v->parent = NULL; + v->type = NCDVAL_MAP; + + MapTree_Init(&v->map.map_tree); + + return v; +} + +static size_t value_map_len (struct value *map) +{ + ASSERT(map->type == NCDVAL_MAP) + + return MapTree_Count(&map->map.map_tree, 0); +} + +static struct value * value_map_at (struct value *map, size_t index) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(index < value_map_len(map)) + + struct value *e = MapTree_GetAt(&map->map.map_tree, 0, index); + ASSERT(e) + ASSERT(e->parent == map) + + return e; +} + +static struct value * value_map_find (struct value *map, NCDValRef key) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(NCDVal_Type(key)) + + struct value *e = MapTree_LookupExact(&map->map.map_tree, 0, key); + ASSERT(!e || e->parent == map) + + return e; +} + +static int value_map_insert (struct value *map, struct value *v, NCDValMem mem, NCDValSafeRef key, NCDModuleInst *i) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(!v->parent) + ASSERT((NCDVal_Type(NCDVal_FromSafe(&mem, key)), 1)) + ASSERT(!value_map_find(map, NCDVal_FromSafe(&mem, key))) + + if (value_map_len(map) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "map has too many elements"); + return 0; + } + + v->map_parent.key_mem = mem; + v->map_parent.key = NCDVal_FromSafe(&v->map_parent.key_mem, key); + int res = MapTree_Insert(&map->map.map_tree, 0, v, NULL); + ASSERT_EXECUTE(res) + v->parent = map; + + return 1; +} + +static void value_map_remove (struct value *map, struct value *v) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(v->parent == map) + + MapTree_Remove(&map->map.map_tree, 0, v); + NCDValMem_Free(&v->map_parent.key_mem); + v->parent = NULL; +} + +static void value_map_remove2 (struct value *map, struct value *v, NCDValMem *out_mem, NCDValSafeRef *out_key) +{ + ASSERT(map->type == NCDVAL_MAP) + ASSERT(v->parent == map) + ASSERT(out_mem) + ASSERT(out_key) + + MapTree_Remove(&map->map.map_tree, 0, v); + *out_mem = v->map_parent.key_mem; + *out_key = NCDVal_ToSafe(v->map_parent.key); + v->parent = NULL; +} + +static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValRef value) +{ + ASSERT((NCDVal_Type(value), 1)) + + struct value *v; + + switch (NCDVal_Type(value)) { + case NCDVAL_STRING: { + if (NCDVal_IsStoredString(value)) { + v = value_init_storedstring(i, NCDVal_StringData(value), NCDVal_StringLength(value)); + } else if (NCDVal_IsIdString(value)) { + v = value_init_idstring(i, NCDVal_IdStringId(value), NCDVal_IdStringStringIndex(value)); + } else if (NCDVal_IsExternalString(value)) { + v = value_init_externalstring(i, NCDVal_StringData(value), NCDVal_StringLength(value), NCDVal_ExternalStringTarget(value)); + } else if (NCDVal_IsComposedString(value)) { + v = value_init_composedstring(i, NCDVal_ComposedStringResource(value), NCDVal_ComposedStringOffset(value), NCDVal_StringLength(value)); + } else { + size_t length = NCDVal_StringLength(value); + v = value_init_storedstring(i, NULL, length); + if (v) { + NCDVal_StringCopyOut(value, 0, length, v->storedstring.data); + } + } + if (!v) { + goto fail0; + } + } break; + + case NCDVAL_LIST: { + if (!(v = value_init_list(i))) { + goto fail0; + } + + size_t count = NCDVal_ListCount(value); + + for (size_t j = 0; j < count; j++) { + struct value *ev = value_init_fromvalue(i, NCDVal_ListGet(value, j)); + if (!ev) { + goto fail1; + } + + if (!value_list_insert(i, v, ev, value_list_len(v))) { + value_cleanup(ev); + goto fail1; + } + } + } break; + + case NCDVAL_MAP: { + if (!(v = value_init_map(i))) { + goto fail0; + } + + for (NCDValMapElem e = NCDVal_MapFirst(value); !NCDVal_MapElemInvalid(e); e = NCDVal_MapNext(value, e)) { + NCDValRef ekey = NCDVal_MapElemKey(value, e); + NCDValRef eval = NCDVal_MapElemVal(value, e); + + NCDValMem key_mem; + NCDValMem_Init(&key_mem); + + NCDValRef key = NCDVal_NewCopy(&key_mem, ekey); + if (NCDVal_IsInvalid(key)) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + struct value *ev = value_init_fromvalue(i, eval); + if (!ev) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + if (!value_map_insert(v, ev, key_mem, NCDVal_ToSafe(key), i)) { + NCDValMem_Free(&key_mem); + value_cleanup(ev); + goto fail1; + } + } + } break; + + default: + ASSERT(0); + return NULL; + } + + return v; + +fail1: + value_cleanup(v); +fail0: + return NULL; +} + +static int value_to_value (NCDModuleInst *i, struct value *v, NCDValMem *mem, NCDValRef *out_value) +{ + ASSERT(mem) + ASSERT(out_value) + + switch (v->type) { + case STOREDSTRING_TYPE: { + *out_value = NCDVal_NewStringBin(mem, (const uint8_t *)v->storedstring.data, v->storedstring.length); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case IDSTRING_TYPE: { + *out_value = NCDVal_NewIdString(mem, v->idstring.id, v->idstring.string_index); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case EXTERNALSTRING_TYPE: { + *out_value = NCDVal_NewExternalString(mem, v->externalstring.data, v->externalstring.length, v->externalstring.ref_target); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case COMPOSEDSTRING_TYPE: { + *out_value = NCDVal_NewComposedString(mem, v->composedstring.resource, v->composedstring.offset, v->composedstring.length); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + } break; + + case NCDVAL_LIST: { + *out_value = NCDVal_NewList(mem, value_list_len(v)); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + + for (size_t index = 0; index < value_list_len(v); index++) { + NCDValRef eval; + if (!value_to_value(i, value_list_at(v, index), mem, &eval)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out_value, eval)) { + goto fail; + } + } + } break; + + case NCDVAL_MAP: { + *out_value = NCDVal_NewMap(mem, value_map_len(v)); + if (NCDVal_IsInvalid(*out_value)) { + goto fail; + } + + for (size_t index = 0; index < value_map_len(v); index++) { + struct value *ev = value_map_at(v, index); + + NCDValRef key = NCDVal_NewCopy(mem, ev->map_parent.key); + if (NCDVal_IsInvalid(key)) { + goto fail; + } + + NCDValRef val; + if (!value_to_value(i, ev, mem, &val)) { + goto fail; + } + + int inserted; + if (!NCDVal_MapInsert(*out_value, key, val, &inserted)) { + goto fail; + } + ASSERT_EXECUTE(inserted) + } + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static struct value * value_get (NCDModuleInst *i, struct value *v, NCDValRef where, int no_error) +{ + ASSERT((NCDVal_Type(where), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + if (!no_error) ModuleLog(i, BLOG_ERROR, "cannot resolve into a string"); + goto fail; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "index is not a valid number (resolving into list)"); + goto fail; + } + + if (index >= value_list_len(v)) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "index is out of bounds (resolving into list)"); + goto fail; + } + + v = value_list_at(v, index); + } break; + + case NCDVAL_MAP: { + v = value_map_find(v, where); + if (!v) { + if (!no_error) ModuleLog(i, BLOG_ERROR, "key does not exist (resolving into map)"); + goto fail; + } + } break; + + default: ASSERT(0); + } + + return v; + +fail: + return NULL; +} + +static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValRef path) +{ + ASSERT(NCDVal_IsList(path)) + + size_t count = NCDVal_ListCount(path); + + for (size_t j = 0; j < count; j++) { + if (!(v = value_get(i, v, NCDVal_ListGet(path, j), 0))) { + goto fail; + } + } + + return v; + +fail: + return NULL; +} + +static struct value * value_insert (NCDModuleInst *i, struct value *v, NCDValRef where, NCDValRef what, int is_replace, struct value **out_oldv) +{ + ASSERT(v) + ASSERT((NCDVal_Type(where), 1)) + ASSERT((NCDVal_Type(what), 1)) + ASSERT(is_replace == !!is_replace) + + struct value *nv = value_init_fromvalue(i, what); + if (!nv) { + goto fail0; + } + + struct value *oldv = NULL; + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + ModuleLog(i, BLOG_ERROR, "cannot insert into a string"); + goto fail1; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + ModuleLog(i, BLOG_ERROR, "index is not a valid number (inserting into list)"); + goto fail1; + } + + if (index > value_list_len(v)) { + ModuleLog(i, BLOG_ERROR, "index is out of bounds (inserting into list)"); + goto fail1; + } + + if (is_replace && index < value_list_len(v)) { + oldv = value_list_at(v, index); + + value_list_remove(v, oldv); + + int res = value_list_insert(i, v, nv, index); + ASSERT_EXECUTE(res) + } else { + if (!value_list_insert(i, v, nv, index)) { + goto fail1; + } + } + } break; + + case NCDVAL_MAP: { + oldv = value_map_find(v, where); + + if (!oldv && value_map_len(v) == SIZE_MAX) { + ModuleLog(i, BLOG_ERROR, "map has too many elements"); + goto fail1; + } + + NCDValMem key_mem; + NCDValMem_Init(&key_mem); + + NCDValRef key = NCDVal_NewCopy(&key_mem, where); + if (NCDVal_IsInvalid(key)) { + NCDValMem_Free(&key_mem); + goto fail1; + } + + if (oldv) { + value_map_remove(v, oldv); + } + + int res = value_map_insert(v, nv, key_mem, NCDVal_ToSafe(key), i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + + if (out_oldv) { + *out_oldv = oldv; + } + else if (oldv) { + value_cleanup(oldv); + } + + return nv; + +fail1: + value_cleanup(nv); +fail0: + return NULL; +} + +static struct value * value_insert_simple (NCDModuleInst *i, struct value *v, NCDValRef what) +{ + ASSERT(v) + ASSERT((NCDVal_Type(what), 1)) + + struct value *nv = value_init_fromvalue(i, what); + if (!nv) { + goto fail0; + } + + switch (v->type) { + case NCDVAL_LIST: { + if (!value_list_insert(i, v, nv, value_list_len(v))) { + goto fail1; + } + } break; + + default: + ModuleLog(i, BLOG_ERROR, "one-argument insert is only defined for lists"); + return NULL; + } + + return nv; + +fail1: + value_cleanup(nv); +fail0: + return NULL; +} + +static int value_remove (NCDModuleInst *i, struct value *v, NCDValRef where) +{ + ASSERT(v) + ASSERT((NCDVal_Type(where), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + ModuleLog(i, BLOG_ERROR, "cannot remove from a string"); + goto fail; + } break; + + case NCDVAL_LIST: { + uintmax_t index; + if (!NCDVal_IsString(where) || !ncd_read_uintmax(where, &index)) { + ModuleLog(i, BLOG_ERROR, "index is not a valid number (removing from list)"); + goto fail; + } + + if (index >= value_list_len(v)) { + ModuleLog(i, BLOG_ERROR, "index is out of bounds (removing from list)"); + goto fail; + } + + struct value *ov = value_list_at(v, index); + + value_list_remove(v, ov); + value_cleanup(ov); + } break; + + case NCDVAL_MAP: { + struct value *ov = value_map_find(v, where); + if (!ov) { + ModuleLog(i, BLOG_ERROR, "key does not exist (removing from map)"); + goto fail; + } + + value_map_remove(v, ov); + value_cleanup(ov); + } break; + + default: ASSERT(0); + } + + return 1; + +fail: + return 0; +} + +static int value_append (NCDModuleInst *i, struct value *v, NCDValRef data) +{ + ASSERT(v) + ASSERT((NCDVal_Type(data), 1)) + + switch (v->type) { + case STOREDSTRING_TYPE: { + if (!NCDVal_IsString(data)) { + ModuleLog(i, BLOG_ERROR, "cannot append non-string to string"); + return 0; + } + + size_t append_length = NCDVal_StringLength(data); + + if (append_length > SIZE_MAX - v->storedstring.length) { + ModuleLog(i, BLOG_ERROR, "too much data to append"); + return 0; + } + size_t new_length = v->storedstring.length + append_length; + + char *new_string = BRealloc(v->storedstring.data, new_length); + if (!new_string) { + ModuleLog(i, BLOG_ERROR, "BRealloc failed"); + return 0; + } + + NCDVal_StringCopyOut(data, 0, append_length, new_string + v->storedstring.length); + + v->storedstring.data = new_string; + v->storedstring.length = new_length; + } break; + + case IDSTRING_TYPE: + case EXTERNALSTRING_TYPE: + case COMPOSEDSTRING_TYPE: { + if (!NCDVal_IsString(data)) { + ModuleLog(i, BLOG_ERROR, "cannot append non-string to string"); + return 0; + } + + size_t length = value_string_length(v); + size_t append_length = NCDVal_StringLength(data); + + if (append_length > SIZE_MAX - length) { + ModuleLog(i, BLOG_ERROR, "too much data to append"); + return 0; + } + size_t new_length = length + append_length; + + char *new_string = BAlloc(new_length); + if (!new_string) { + ModuleLog(i, BLOG_ERROR, "BAlloc failed"); + return 0; + } + + value_string_copy_out(v, new_string); + NCDVal_StringCopyOut(data, 0, append_length, new_string + length); + + value_string_set_allocd(v, new_string, new_length); + } break; + + case NCDVAL_LIST: { + struct value *nv = value_init_fromvalue(i, data); + if (!nv) { + return 0; + } + + if (!value_list_insert(i, v, nv, value_list_len(v))) { + value_cleanup(nv); + return 0; + } + } break; + + default: + ModuleLog(i, BLOG_ERROR, "append is only defined for strings and lists"); + return 0; + } + + return 1; +} + +static void valref_init (struct valref *r, struct value *v) +{ + r->v = v; + + if (v) { + LinkedList0_Prepend(&v->refs_list, &r->refs_list_node); + } +} + +static void valref_free (struct valref *r) +{ + if (r->v) { + LinkedList0_Remove(&r->v->refs_list, &r->refs_list_node); + value_cleanup(r->v); + } +} + +static struct value * valref_val (struct valref *r) +{ + return r->v; +} + +static void valref_break (struct valref *r) +{ + ASSERT(r->v) + + LinkedList0_Remove(&r->v->refs_list, &r->refs_list_node); + r->v = NULL; +} + +static void func_new_common (void *vo, NCDModuleInst *i, struct value *v, value_deinit_func deinit_func, void *deinit_data) +{ + struct instance *o = vo; + o->i = i; + + // init value references + valref_init(&o->ref, v); + + // remember deinit + o->deinit_func = deinit_func; + o->deinit_data = deinit_data; + + NCDModuleInst_Backend_Up(i); + return; +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // deinit + if (o->deinit_func) { + o->deinit_func(o->deinit_data, o->i); + } + + // free value reference + valref_free(&o->ref); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + struct value *v = valref_val(&o->ref); + + if (name == ModuleString(o->i, STRING_EXISTS)) { + *out = ncd_make_boolean(mem, !!v, o->i->params->iparams->string_index); + return 1; + } + + if (name != NCD_STRING_TYPE && name != NCD_STRING_LENGTH && + name != ModuleString(o->i, STRING_KEYS) && name != NCD_STRING_EMPTY) { + return 0; + } + + if (!v) { + ModuleLog(o->i, BLOG_ERROR, "value was deleted"); + return 0; + } + + if (name == NCD_STRING_TYPE) { + *out = NCDVal_NewString(mem, get_type_str(v->type)); + } + else if (name == NCD_STRING_LENGTH) { + size_t len = 0; // to remove warning + switch (v->type) { + case NCDVAL_LIST: + len = value_list_len(v); + break; + case NCDVAL_MAP: + len = value_map_len(v); + break; + default: + ASSERT(value_is_string(v)) + len = value_string_length(v); + break; + } + + *out = ncd_make_uintmax(mem, len); + } + else if (name == ModuleString(o->i, STRING_KEYS)) { + if (v->type != NCDVAL_MAP) { + ModuleLog(o->i, BLOG_ERROR, "value is not a map (reading keys variable)"); + return 0; + } + + *out = NCDVal_NewList(mem, value_map_len(v)); + if (NCDVal_IsInvalid(*out)) { + goto fail; + } + + for (size_t j = 0; j < value_map_len(v); j++) { + struct value *ev = value_map_at(v, j); + + NCDValRef key = NCDVal_NewCopy(mem, ev->map_parent.key); + if (NCDVal_IsInvalid(key)) { + goto fail; + } + + if (!NCDVal_ListAppend(*out, key)) { + goto fail; + } + } + } + else if (name == NCD_STRING_EMPTY) { + if (!value_to_value(o->i, v, mem, out)) { + return 0; + } + } + else { + ASSERT(0); + } + + return 1; + +fail: + *out = NCDVal_NewInvalid(); + return 1; +} + +static void func_new_value (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_get (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get(i, mov, where_arg, 0); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_try_get (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get(i, mov, where_arg, 1); + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_getpath (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef path_arg; + if (!NCDVal_ListRead(params->args, 1, &path_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsList(path_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_get_path(i, mov, path_arg); + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert_replace_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_replace) +{ + NCDValRef where_arg = NCDVal_NewInvalid(); + NCDValRef what_arg; + if (!(!is_replace && NCDVal_ListRead(params->args, 1, &what_arg)) && + !NCDVal_ListRead(params->args, 2, &where_arg, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v; + + if (NCDVal_IsInvalid(where_arg)) { + v = value_insert_simple(i, mov, what_arg); + } else { + v = value_insert(i, mov, where_arg, what_arg, is_replace, NULL); + } + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_common(vo, i, params, 0); +} + +static void func_new_replace (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_common(vo, i, params, 1); +} + +struct insert_undo_deinit_data { + struct valref val_ref; + struct valref oldval_ref; +}; + +static void undo_deinit_func (struct insert_undo_deinit_data *data, NCDModuleInst *i) +{ + struct value *val = valref_val(&data->val_ref); + struct value *oldval = valref_val(&data->oldval_ref); + + if (val && val->parent && (!oldval || !oldval->parent)) { + // get parent + struct value *parent = val->parent; + + // remove this value from parent and restore saved one (or none) + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, val); + value_list_remove(parent, val); + if (oldval) { + int res = value_list_insert(i, parent, oldval, index); + ASSERT_EXECUTE(res) + } + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, val, &key_mem, &key); + if (oldval) { + int res = value_map_insert(parent, oldval, key_mem, key, i); + ASSERT_EXECUTE(res) + } else { + NCDValMem_Free(&key_mem); + } + } break; + + default: ASSERT(0); + } + } + + valref_free(&data->oldval_ref); + valref_free(&data->val_ref); + free(data); +} + +static void func_new_insert_replace_undo_common (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, int is_replace) +{ + NCDValRef where_arg = NCDVal_NewInvalid(); + NCDValRef what_arg; + if (!(!is_replace && NCDVal_ListRead(params->args, 1, &what_arg)) && + !NCDVal_ListRead(params->args, 2, &where_arg, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct insert_undo_deinit_data *data = malloc(sizeof(*data)); + if (!data) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + struct value *oldv; + struct value *v; + + if (NCDVal_IsInvalid(where_arg)) { + oldv = NULL; + v = value_insert_simple(i, mov, what_arg); + } else { + v = value_insert(i, mov, where_arg, what_arg, is_replace, &oldv); + } + if (!v) { + goto fail1; + } + + valref_init(&data->val_ref, v); + valref_init(&data->oldval_ref, oldv); + + func_new_common(vo, i, v, (value_deinit_func)undo_deinit_func, data); + return; + +fail1: + free(data); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_insert_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_undo_common(vo, i, params, 0); +} + +static void func_new_replace_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + func_new_insert_replace_undo_common(vo, i, params, 1); +} + +static void func_new_replace_this (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + if (mov->parent) { + struct value *parent = mov->parent; + + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, mov); + value_list_remove(parent, mov); + int res = value_list_insert(i, parent, v, index); + ASSERT_EXECUTE(res) + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, mov, &key_mem, &key); + int res = value_map_insert(parent, v, key_mem, key, i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + + value_cleanup(mov); + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_replace_this_undo (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + struct value *v = value_init_fromvalue(i, value_arg); + if (!v) { + goto fail0; + } + + struct insert_undo_deinit_data *data = malloc(sizeof(*data)); + if (!data) { + ModuleLog(i, BLOG_ERROR, "malloc failed"); + goto fail1; + } + + valref_init(&data->val_ref, v); + valref_init(&data->oldval_ref, mov); + + if (mov->parent) { + struct value *parent = mov->parent; + + switch (parent->type) { + case NCDVAL_LIST: { + size_t index = value_list_indexof(parent, mov); + value_list_remove(parent, mov); + int res = value_list_insert(i, parent, v, index); + ASSERT_EXECUTE(res) + } break; + + case NCDVAL_MAP: { + NCDValMem key_mem; + NCDValSafeRef key; + value_map_remove2(parent, mov, &key_mem, &key); + int res = value_map_insert(parent, v, key_mem, key, i); + ASSERT_EXECUTE(res) + } break; + + default: ASSERT(0); + } + } + + func_new_common(vo, i, v, (value_deinit_func)undo_deinit_func, data); + return; + +fail1: + value_cleanup(v); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_new_substr (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef start_arg; + NCDValRef length_arg = NCDVal_NewInvalid(); + if (!NCDVal_ListRead(params->args, 1, &start_arg) && + !NCDVal_ListRead(params->args, 2, &start_arg, &length_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + if (!NCDVal_IsString(start_arg) || (!NCDVal_IsInvalid(length_arg) && !NCDVal_IsString(length_arg))) { + ModuleLog(i, BLOG_ERROR, "wrong type"); + goto fail0; + } + + uintmax_t start; + if (!ncd_read_uintmax(start_arg, &start)) { + ModuleLog(i, BLOG_ERROR, "start is not a number"); + goto fail0; + } + + uintmax_t length = UINTMAX_MAX; + if (!NCDVal_IsInvalid(length_arg) && !ncd_read_uintmax(length_arg, &length)) { + ModuleLog(i, BLOG_ERROR, "length is not a number"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_is_string(mov)) { + ModuleLog(i, BLOG_ERROR, "value is not a string"); + goto fail0; + } + + size_t string_len = value_string_length(mov); + + if (start > string_len) { + ModuleLog(i, BLOG_ERROR, "start is out of range"); + goto fail0; + } + + size_t remain = string_len - start; + size_t amount = length < remain ? length : remain; + + struct value *v = NULL; + switch (mov->type) { + case STOREDSTRING_TYPE: { + v = value_init_storedstring(i, mov->storedstring.data + start, amount); + } break; + case IDSTRING_TYPE: { + v = value_init_storedstring(i, NCDStringIndex_Value(mov->idstring.string_index, mov->idstring.id) + start, amount); + } break; + case EXTERNALSTRING_TYPE: { + v = value_init_externalstring(i, mov->externalstring.data + start, amount, mov->externalstring.ref_target); + } break; + case COMPOSEDSTRING_TYPE: { + v = value_init_composedstring(i, mov->composedstring.resource, mov->composedstring.offset + start, amount); + } break; + default: + ASSERT(0); + } + + if (!v) { + goto fail0; + } + + func_new_common(vo, i, v, NULL, NULL); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void remove_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef where_arg; + if (!NCDVal_ListRead(params->args, 1, &where_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_remove(i, mov, where_arg)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void delete_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + if (!NCDVal_ListRead(params->args, 0)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + value_delete(mov); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void reset_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef what_arg; + if (!NCDVal_ListRead(params->args, 1, &what_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // build value from argument + struct value *newv = value_init_fromvalue(i, what_arg); + if (!newv) { + goto fail0; + } + + // deinit + if (mo->deinit_func) { + mo->deinit_func(mo->deinit_data, i); + } + + // free value reference + valref_free(&mo->ref); + + // set up value reference + valref_init(&mo->ref, newv); + + // set no deinit function + mo->deinit_func = NULL; + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void append_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + NCDValRef data_arg; + if (!NCDVal_ListRead(params->args, 1, &data_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + struct value *mov = valref_val(&mo->ref); + + if (!mov) { + ModuleLog(i, BLOG_ERROR, "value was deleted"); + goto fail0; + } + + if (!value_append(i, mov, data_arg)) { + goto fail0; + } + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "value", + .func_new2 = func_new_value, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::get", + .base_type = "value", + .func_new2 = func_new_get, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::try_get", + .base_type = "value", + .func_new2 = func_new_try_get, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::getpath", + .base_type = "value", + .func_new2 = func_new_getpath, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::insert", + .base_type = "value", + .func_new2 = func_new_insert, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace", + .base_type = "value", + .func_new2 = func_new_replace, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_this", + .base_type = "value", + .func_new2 = func_new_replace_this, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::insert_undo", + .base_type = "value", + .func_new2 = func_new_insert_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_undo", + .base_type = "value", + .func_new2 = func_new_replace_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::replace_this_undo", + .base_type = "value", + .func_new2 = func_new_replace_this_undo, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::remove", + .func_new2 = remove_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::delete", + .func_new2 = delete_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::reset", + .func_new2 = reset_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::substr", + .base_type = "value", + .func_new2 = func_new_substr, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance), + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = "value::append", + .func_new2 = append_func_new, + .flags = NCDMODULE_FLAG_ACCEPT_NON_CONTINUOUS_STRINGS + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_value = { + .modules = modules, + .strings = strings +}; diff --git a/external/badvpn_dns/ncd/modules/value_maptree.h b/external/badvpn_dns/ncd/modules/value_maptree.h new file mode 100644 index 00000000..e6f69f93 --- /dev/null +++ b/external/badvpn_dns/ncd/modules/value_maptree.h @@ -0,0 +1,11 @@ +#define SAVL_PARAM_NAME MapTree +#define SAVL_PARAM_FEATURE_COUNTS 1 +#define SAVL_PARAM_FEATURE_NOKEYS 0 +#define SAVL_PARAM_TYPE_ENTRY struct value +#define SAVL_PARAM_TYPE_KEY NCDValRef +#define SAVL_PARAM_TYPE_ARG int +#define SAVL_PARAM_TYPE_COUNT size_t +#define SAVL_PARAM_VALUE_COUNT_MAX SIZE_MAX +#define SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) NCDVal_Compare((entry1)->map_parent.key, (entry2)->map_parent.key) +#define SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) NCDVal_Compare((key1), (entry2)->map_parent.key) +#define SAVL_PARAM_MEMBER_NODE map_parent.maptree_node diff --git a/external/badvpn_dns/ncd/modules/valuemetic.c b/external/badvpn_dns/ncd/modules/valuemetic.c new file mode 100644 index 00000000..a87f73bf --- /dev/null +++ b/external/badvpn_dns/ncd/modules/valuemetic.c @@ -0,0 +1,219 @@ +/** + * @file valuemetic.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Comparison functions for values. + * + * Synopsis: + * val_lesser(v1, v2) + * val_greater(v1, v2) + * val_lesser_equal(v1, v2) + * val_greater_equal(v1, v2) + * val_equal(v1, v2) + * val_different(v1, v2) + * + * Variables: + * (empty) - "true" or "false", reflecting the value of the relation in question + * + * Description: + * These statements perform comparisons of values. Order of values is defined by the + * following rules: + * 1. Values of different types have the following order: strings, lists, maps. + * 2. String values are ordered lexicographically, with respect to the numeric values + * of their bytes. + * 3. List values are ordered lexicographically, where the order of the elements is + * defined by recursive application of these rules. + * 4. Map values are ordered lexicographically, as if a map was a list of (key, value) + * pairs ordered by key, where the order of both keys and values is defined by + * recursive application of these rules. + */ + +#include +#include + +#include +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + int result; +}; + +typedef int (*compute_func) (NCDValRef v1, NCDValRef v2); + +static int compute_lesser (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) < 0; +} + +static int compute_greater (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) > 0; +} + +static int compute_lesser_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) <= 0; +} + +static int compute_greater_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) >= 0; +} + +static int compute_equal (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) == 0; +} + +static int compute_different (NCDValRef v1, NCDValRef v2) +{ + return NCDVal_Compare(v1, v2) != 0; +} + +static void new_templ (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params, compute_func cfunc) +{ + struct instance *o = vo; + o->i = i; + + NCDValRef v1_arg; + NCDValRef v2_arg; + if (!NCDVal_ListRead(params->args, 2, &v1_arg, &v2_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + o->result = cfunc(v1_arg, v2_arg); + + NCDModuleInst_Backend_Up(i); + return; + +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = ncd_make_boolean(mem, o->result, o->i->params->iparams->string_index); + return 1; + } + + return 0; +} + +static void func_new_lesser (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_lesser); +} + +static void func_new_greater (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_greater); +} + +static void func_new_lesser_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_lesser_equal); +} + +static void func_new_greater_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_greater_equal); +} + +static void func_new_equal (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_equal); +} + +static void func_new_different (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + new_templ(vo, i, params, compute_different); +} + +static struct NCDModule modules[] = { + { + .type = "val_lesser", + .func_new2 = func_new_lesser, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_greater", + .func_new2 = func_new_greater, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_lesser_equal", + .func_new2 = func_new_lesser_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_greater_equal", + .func_new2 = func_new_greater_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_equal", + .func_new2 = func_new_equal, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "val_different", + .func_new2 = func_new_different, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_valuemetic = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/modules/var.c b/external/badvpn_dns/ncd/modules/var.c new file mode 100644 index 00000000..83abd44d --- /dev/null +++ b/external/badvpn_dns/ncd/modules/var.c @@ -0,0 +1,163 @@ +/** + * @file var.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Variable module. + * + * Synopsis: var(value) + * Variables: + * (empty) - value + * + * Synopsis: var::set(value) + */ + +#include +#include + +#include +#include + +#include + +#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__) + +struct instance { + NCDModuleInst *i; + NCDValMem mem; + NCDValRef value; +}; + +static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + struct instance *o = vo; + o->i = i; + + // read argument + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(o->i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // init mem + NCDValMem_Init(&o->mem); + + // copy value + o->value = NCDVal_NewCopy(&o->mem, value_arg); + if (NCDVal_IsInvalid(o->value)) { + goto fail1; + } + + // signal up + NCDModuleInst_Backend_Up(o->i); + return; + +fail1: + NCDValMem_Free(&o->mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static void func_die (void *vo) +{ + struct instance *o = vo; + + // free mem + NCDValMem_Free(&o->mem); + + NCDModuleInst_Backend_Dead(o->i); +} + +static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out) +{ + struct instance *o = vo; + + if (name == NCD_STRING_EMPTY) { + *out = NCDVal_NewCopy(mem, o->value); + return 1; + } + + return 0; +} + +static void set_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params) +{ + // read arguments + NCDValRef value_arg; + if (!NCDVal_ListRead(params->args, 1, &value_arg)) { + ModuleLog(i, BLOG_ERROR, "wrong arity"); + goto fail0; + } + + // get method object + struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user); + + // allocate new mem + NCDValMem mem; + NCDValMem_Init(&mem); + + // copy value + NCDValRef copy = NCDVal_NewCopy(&mem, value_arg); + if (NCDVal_IsInvalid(copy)) { + goto fail1; + } + + // replace value in var + NCDValMem_Free(&mo->mem); + mo->mem = mem; + mo->value = NCDVal_Moved(&mo->mem, copy); + + // signal up + NCDModuleInst_Backend_Up(i); + return; + +fail1: + NCDValMem_Free(&mem); +fail0: + NCDModuleInst_Backend_DeadError(i); +} + +static struct NCDModule modules[] = { + { + .type = "var", + .func_new2 = func_new, + .func_die = func_die, + .func_getvar2 = func_getvar2, + .alloc_size = sizeof(struct instance) + }, { + .type = "var::set", + .func_new2 = set_func_new + }, { + .type = NULL + } +}; + +const struct NCDModuleGroup ncdmodule_var = { + .modules = modules +}; diff --git a/external/badvpn_dns/ncd/ncd.c b/external/badvpn_dns/ncd/ncd.c new file mode 100644 index 00000000..b3270fc6 --- /dev/null +++ b/external/badvpn_dns/ncd/ncd.c @@ -0,0 +1,463 @@ +/** + * @file ncd.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_SYSLOG +#include +#endif + +#include "ncd.h" + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_STDERR 2 +#define LOGGER_SYSLOG 3 + +// command-line options +static struct { + int help; + int version; + int logger; +#ifdef BADVPN_USE_SYSLOG + char *logger_syslog_facility; + char *logger_syslog_ident; +#endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *config_file; + int syntax_only; + int retry_time; + int no_udev; + char **extra_args; + int num_extra_args; +} options; + +// reactor +static BReactor reactor; + +// process manager +static BProcessManager manager; + +// udev manager +static NCDUdevManager umanager; + +// random number generator +static BRandom2 random2; + +// interpreter +static NCDInterpreter interpreter; + +// forward declarations of functions +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static void signal_handler (void *unused); +static void interpreter_handler_finished (void *user, int exit_code); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + int main_exit_code = 1; + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + case LOGGER_STDERR: + BLog_InitStderr(); + break; +#ifdef BADVPN_USE_SYSLOG + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; +#endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } else { + BLog_SetChannelLoglevel(i, DEFAULT_LOGLEVEL); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&reactor)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // init process manager + if (!BProcessManager_Init(&manager, &reactor)) { + BLog(BLOG_ERROR, "BProcessManager_Init failed"); + goto fail2; + } + + // init udev manager + NCDUdevManager_Init(&umanager, options.no_udev, &reactor, &manager); + + // init random number generator + if (!BRandom2_Init(&random2, BRANDOM2_INIT_LAZY)) { + BLog(BLOG_ERROR, "BRandom2_Init failed"); + goto fail3; + } + + // setup signal handler + if (!BSignal_Init(&reactor, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail4; + } + + // build program + NCDProgram program; + if (!NCDBuildProgram_Build(options.config_file, &program)) { + BLog(BLOG_ERROR, "failed to build program"); + goto fail5; + } + + // setup interpreter parameters + struct NCDInterpreter_params params; + params.handler_finished = interpreter_handler_finished; + params.user = NULL; + params.retry_time = options.retry_time; + params.extra_args = options.extra_args; + params.num_extra_args = options.num_extra_args; + params.reactor = &reactor; + params.manager = &manager; + params.umanager = &umanager; + params.random2 = &random2; + + // initialize interpreter + if (!NCDInterpreter_Init(&interpreter, program, params)) { + goto fail5; + } + + // don't enter event loop if syntax check is requested + if (options.syntax_only) { + main_exit_code = 0; + goto fail6; + } + + BLog(BLOG_NOTICE, "entering event loop"); + + // enter event loop + main_exit_code = BReactor_Exec(&reactor); + +fail6: + // free interpreter + NCDInterpreter_Free(&interpreter); +fail5: + // remove signal handler + BSignal_Finish(); +fail4: + // free random number generator + BRandom2_Free(&random2); +fail3: + // free udev manager + NCDUdevManager_Free(&umanager); + + // free process manager + BProcessManager_Free(&manager); +fail2: + // free reactor + BReactor_Free(&reactor); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish objects + DebugObjectGlobal_Finish(); + + return main_exit_code; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger ]\n" + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--retry-time ]\n" + " [--no-udev]\n" + " [--config-file ]\n" + " [--syntax-only]\n" + " [-- program_args...]\n" + " [ program_args...]\n" , + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDERR; +#ifdef BADVPN_USE_SYSLOG + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; +#endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.config_file = NULL; + options.syntax_only = 0; + options.retry_time = DEFAULT_RETRY_TIME; + options.no_udev = 0; + options.extra_args = NULL; + options.num_extra_args = 0; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + else if (!strcmp(arg2, "stderr")) { + options.logger = LOGGER_STDERR; + } +#ifdef BADVPN_USE_SYSLOG + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } +#endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } +#ifdef BADVPN_USE_SYSLOG + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } +#endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--config-file")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.config_file = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syntax-only")) { + options.syntax_only = 1; + } + else if (!strcmp(arg, "--retry-time")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.retry_time = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--no-udev")) { + options.no_udev = 1; + } + else if (!strcmp(arg, "--")) { + options.extra_args = &argv[i + 1]; + options.num_extra_args = argc - i - 1; + i += options.num_extra_args; + } + else if (!string_begins_with(arg, "--")) { + if (options.config_file) { + fprintf(stderr, "%s: program is already specified (did you mean to use -- ?)\n", arg); + return 0; + } + options.config_file = argv[i]; + options.extra_args = &argv[i + 1]; + options.num_extra_args = argc - i - 1; + i += options.num_extra_args; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.config_file) { + fprintf(stderr, "No program is specified.\n"); + return 0; + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + NCDInterpreter_RequestShutdown(&interpreter, 1); +} + +void interpreter_handler_finished (void *user, int exit_code) +{ + BReactor_Quit(&reactor, exit_code); +} diff --git a/external/badvpn_dns/ncd/ncd.h b/external/badvpn_dns/ncd/ncd.h new file mode 100644 index 00000000..88c40706 --- /dev/null +++ b/external/badvpn_dns/ncd/ncd.h @@ -0,0 +1,37 @@ +/** + * @file ncd.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "ncd" + +// how long to wait after an error before retrying +#define DEFAULT_RETRY_TIME 5000 + +// default loglevel +#define DEFAULT_LOGLEVEL BLOG_WARNING diff --git a/external/badvpn_dns/ncd/parse_linux_input.sh b/external/badvpn_dns/ncd/parse_linux_input.sh new file mode 100755 index 00000000..f277484c --- /dev/null +++ b/external/badvpn_dns/ncd/parse_linux_input.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +INPUT=$1 +OUTPUT=$2 + +types="" +keys="" +rels="" +abss="" +sws="" +mscs="" +leds="" +reps="" +snds="" +ffstatuss="" + +while read LINE; do + tab=$'\t' + space="[ ${tab}]" + regex="^#define ((EV|KEY|BTN|REL|ABS|SW|MSC|LED|REP|SND|FF_STATUS)_[A-Z0-9_]+)${space}" + if [[ $LINE =~ $regex ]]; then + type=${BASH_REMATCH[2]} + name=${BASH_REMATCH[1]} + if [[ $type = "EV" ]]; then + if [[ $name != "EV_VERSION" ]]; then + types="${types} [${name}] = \"${name}\", +" + fi + elif [[ $type = "KEY" ]] || [[ $type = "BTN" ]]; then + if [[ $name != "KEY_MIN_INTERESTING" ]]; then + keys="${keys} [${name}] = \"${name}\", +" + fi + elif [[ $type = "REL" ]]; then + rels="${rels} [${name}] = \"${name}\", +" + elif [[ $type = "ABS" ]]; then + abss="${abss} [${name}] = \"${name}\", +" + elif [[ $type = "SW" ]]; then + sws="${sws} [${name}] = \"${name}\", +" + elif [[ $type = "MSC" ]]; then + mscs="${mscs} [${name}] = \"${name}\", +" + elif [[ $type = "LED" ]]; then + leds="${leds} [${name}] = \"${name}\", +" + elif [[ $type = "REP" ]]; then + reps="${reps} [${name}] = \"${name}\", +" + elif [[ $type = "SND" ]]; then + snds="${snds} [${name}] = \"${name}\", +" + elif [[ $type = "FF_STATUS" ]]; then + ffstatuss="${ffstatuss} [${name}] = \"${name}\", +" + fi + fi +done < "${INPUT}" + +( +echo " +static const char *type_names[] = { +${types}}; + +static const char *key_names[] = { +${keys}}; + +static const char *rel_names[] = { +${rels}}; + +static const char *abs_names[] = { +${abss}}; + +static const char *sw_names[] = { +${sws}}; + +static const char *msc_names[] = { +${mscs}}; + +static const char *led_names[] = { +${leds}}; + +static const char *rep_names[] = { +${reps}}; + +static const char *snd_names[] = { +${snds}}; + +static const char *ffstatus_names[] = { +${ffstatuss}}; +" +) >"${OUTPUT}" diff --git a/external/badvpn_dns/ncd/static_strings.h b/external/badvpn_dns/ncd/static_strings.h new file mode 100644 index 00000000..a8232247 --- /dev/null +++ b/external/badvpn_dns/ncd/static_strings.h @@ -0,0 +1,70 @@ +/** + * @file static_strings.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STATIC_STRINGS_H +#define BADVPN_STATIC_STRINGS_H + +// NOTE: keep synchronized with NCDStringIndex.c +enum { + NCD_STRING_EMPTY, + NCD_STRING_ARGS, + NCD_STRING_ARG0, + NCD_STRING_ARG1, + NCD_STRING_ARG2, + NCD_STRING_ARG3, + NCD_STRING_ARG4, + NCD_STRING_ARG5, + NCD_STRING_ARG6, + NCD_STRING_ARG7, + NCD_STRING_ARG8, + NCD_STRING_ARG9, + NCD_STRING_ARG10, + NCD_STRING_ARG11, + NCD_STRING_ARG12, + NCD_STRING_ARG13, + NCD_STRING_ARG14, + NCD_STRING_ARG15, + NCD_STRING_ARG16, + NCD_STRING_ARG17, + NCD_STRING_ARG18, + NCD_STRING_ARG19, + NCD_STRING_TRUE, + NCD_STRING_FALSE, + NCD_STRING_NONE, + NCD_STRING_CALLER, + NCD_STRING_SUCCEEDED, + NCD_STRING_IS_ERROR, + NCD_STRING_NOT_EOF, + NCD_STRING_LENGTH, + NCD_STRING_TYPE, + NCD_STRING_EXIT_STATUS, + NCD_STRING_SIZE +}; + +#endif diff --git a/external/badvpn_dns/ncd/tests/addr_in_network.ncd b/external/badvpn_dns/ncd/tests/addr_in_network.ncd new file mode 100644 index 00000000..fc532921 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/addr_in_network.ncd @@ -0,0 +1,60 @@ +process main { + net.ipv4.addr_in_network("192.168.6.0", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.0", "192.168.6.0/24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.1", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.6.255", "192.168.6.0", "24") r; + assert(r); + + net.ipv4.addr_in_network("192.168.5.255", "192.168.6.0", "24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("192.168.7.0", "192.168.6.0", "24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("192.168.7.0", "192.168.6.0/24") r; + not(r) r; + assert(r); + + net.ipv4.addr_in_network("0.0.0.0", "192.168.6.0", "0") r; + assert(r); + + net.ipv4.addr_in_network("0.0.0.0", "0.0.0.0", "0") r; + assert(r); + + net.ipv4.addr_in_network("255.255.255.255", "0.0.0.0", "0") r; + assert(r); + + net.ipv6.addr_in_network("::123:0", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:1", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:ffff", "::123:0/112") r; + assert(r); + + net.ipv6.addr_in_network("::123:ffff", "::123:ffff/128") r; + assert(r); + + net.ipv6.addr_in_network("::122:ffff", "::123:0/112") r; + not(r) r; + assert(r); + + net.ipv6.addr_in_network("::124:0", "::123:0/112") r; + not(r) r; + assert(r); + + net.ipv6.addr_in_network("::123:fffe", "::123:ffff/128") r; + not(r) r; + assert(r); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/alias.ncd b/external/badvpn_dns/ncd/tests/alias.ncd new file mode 100644 index 00000000..624a4edf --- /dev/null +++ b/external/badvpn_dns/ncd/tests/alias.ncd @@ -0,0 +1,48 @@ +process foo { + var("hello") x; + alias("x") y; + val_equal(y, "hello") a; + assert(a); + + var("hello") x; + alias("x") y; + y->set("world"); + val_equal(y, "world") a; + assert(a); + + var("hello") x; + alias("x") y; + alias("y") z; + z->set("world"); + val_equal(x, "world") a; + assert(a); + + call("test", {"hello"}) c; + alias("c.x") x; + val_equal(x, "hello") a; + assert(a); + + call("test", {"hello"}) c; + alias("c") x; + alias("x") y; + alias("y.x") z; + c.x->set("world"); + val_equal(z, "world") a; + assert(a); + + var("hello") x; + call("test2", {"_caller.x"}) c; + c.x->set("world"); + val_equal(x, "world") a; + assert(a); + + exit("0"); +} + +template test { + var(_arg0) x; +} + +template test2 { + alias(_arg0) x; +} diff --git a/external/badvpn_dns/ncd/tests/arithmetic.ncd b/external/badvpn_dns/ncd/tests/arithmetic.ncd new file mode 100644 index 00000000..8f82bcc8 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/arithmetic.ncd @@ -0,0 +1,69 @@ +process main { + num_lesser("6", "7") r; + assert(r); + + num_lesser("7", "7") r; + not(r) a; + assert(a); + + num_greater("7", "6") r; + assert(r); + + num_greater("7", "7") r; + not(r) a; + assert(a); + + num_lesser_equal("7", "7") r; + assert(r); + + num_lesser_equal("8", "7") r; + not(r) a; + assert(a); + + num_greater_equal("7", "7") r; + assert(r); + + num_greater_equal("7", "8") r; + not(r) a; + assert(a); + + num_equal("7", "7") r; + assert(r); + + num_equal("6", "7") r; + not(r) a; + assert(a); + + num_equal("7", "6") r; + not(r) a; + assert(a); + + num_different("7", "6") a; + assert(a); + + num_different("7", "007") a; + not(a) a; + assert(a); + + num_add("4", "7") r; + strcmp(r, "11") a; + assert(a); + + num_subtract("4", "3") r; + strcmp(r, "1") a; + assert(a); + + num_multiply("4", "5") r; + strcmp(r, "20") a; + assert(a); + + num_divide("7", "3") r; + strcmp(r, "2") a; + assert(a); + + num_modulo("7", "3") r; + strcmp(r, "1") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/backtracking.ncd b/external/badvpn_dns/ncd/tests/backtracking.ncd new file mode 100644 index 00000000..1faf3b7c --- /dev/null +++ b/external/badvpn_dns/ncd/tests/backtracking.ncd @@ -0,0 +1,31 @@ +process main { + value({}) list; + var("0") i; + backtrack_point() point; + num_lesser(i, "100") do_more; + If (do_more) { + list->insert(i); + num_add(i, "1") new_i; + i->set(new_i); + point->go(); + }; + val_equal(list.length, "100") a; + assert(a); + + value({}) list; + var("0") i; + blocker() blk; + blk->up(); + blk->use(); + num_lesser(i, "100") do_more; + If (do_more) { + list->insert(i); + num_add(i, "1") new_i; + i->set(new_i); + blk->downup(); + }; + val_equal(list.length, "100") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/buffer.ncd b/external/badvpn_dns/ncd/tests/buffer.ncd new file mode 100644 index 00000000..1af0ea76 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/buffer.ncd @@ -0,0 +1,54 @@ +process main { + buffer() buf; + val_equal(buf, "") a; + assert(a); + + buf->append("12"); + val_equal(buf, "12") a; + assert(a); + + buf->append("345"); + val_equal(buf, "12345") a; + assert(a); + + buf->consume("1"); + val_equal(buf, "2345") a; + assert(a); + + buf->consume("1"); + val_equal(buf, "345") a; + assert(a); + + buf->consume("3"); + val_equal(buf, "") a; + assert(a); + + buf->append("6"); + val_equal(buf, "6") a; + assert(a); + + buf->append("7890"); + val_equal(buf, "67890") a; + assert(a); + + buf->append(""); + val_equal(buf, "67890") a; + assert(a); + + buf->consume("4"); + val_equal(buf, "0") a; + assert(a); + + buf->append("1234567890"); + val_equal(buf, "01234567890") a; + assert(a); + + val_equal(buf.length, "11") a; + assert(a); + + buffer("hello") buf2; + val_equal(buf2, "hello") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/call.ncd b/external/badvpn_dns/ncd/tests/call.ncd new file mode 100644 index 00000000..273ed68c --- /dev/null +++ b/external/badvpn_dns/ncd/tests/call.ncd @@ -0,0 +1,18 @@ +process main { + var("bad_x") x; + var("good_x") y; + call("helper_func", {}) helper; + call_with_caller_target("func1", {}, "helper") c; + val_equal(c.x, "good_x") a; + assert(a); + + exit("0"); +} + +template helper_func { + var(_caller.y) x; +} + +template func1 { + var(_caller.x) x; +} diff --git a/external/badvpn_dns/ncd/tests/concat.ncd b/external/badvpn_dns/ncd/tests/concat.ncd new file mode 100644 index 00000000..72b256e1 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/concat.ncd @@ -0,0 +1,19 @@ +process main { + concat("Hello", "", "World") x; + strcmp(x, "HelloWorld") a; + assert(a); + + concat("\x00\x00", "\x00") x; + strcmp(x, "\x00\x00\x00") a; + assert(a); + + concatv({"Hello", "", "World"}) x; + strcmp(x, "HelloWorld") a; + assert(a); + + concatv({"\x00\x00", "\x00"}) x; + strcmp(x, "\x00\x00\x00") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/depend.ncd b/external/badvpn_dns/ncd/tests/depend.ncd new file mode 100644 index 00000000..c1a34b67 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/depend.ncd @@ -0,0 +1,64 @@ +process main { + var("hello") x; + provide("A"); + depend("A") d; + val_equal(d.x, "hello") a; + assert(a); + d.x->set("world"); + val_equal(d.x, "world") a; + assert(a); + + var("hello") x; + provide("B"); + val_equal(x, "world") a; + assert(a); + + var("hello") x; + provide("C"); + val_equal(x, "hello") a; + assert(a); + depend("C_done"); + val_equal(x, "world") a; + assert(a); + + var("hello") x; + blocker() blk; + provide("D"); + val_equal(x, "hello") a; + assert(a); + blk->up(); + val_equal(x, "0") a; + assert(a); + blk->down(); + blk->up(); + val_equal(x, "1") a; + assert(a); + + exit("0"); +} + +process proc1 { + depend("B") dep; + dep.x->set("world"); +} + +process proc2 { + depend("C") dep; + sleep("0", "0"); + dep.x->set("world"); + provide("C_done"); +} + +process proc3 { + depend("D") dep; + dep.blk->use(); + provide("E"); +} + +process proc4 { + var("0") i; + depend("E") dep; + dep.dep.x->set(i); + num_add(i, "1") j; + i->set(j); +} diff --git a/external/badvpn_dns/ncd/tests/depend_scope.ncd b/external/badvpn_dns/ncd/tests/depend_scope.ncd new file mode 100644 index 00000000..a29a9a69 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/depend_scope.ncd @@ -0,0 +1,31 @@ +process main { + depend_scope() scope; + var("0") x; + process_manager() mgr; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "2") a; # must not have rebound temporarily to A during backtracking + assert(a); + exit("0"); + }; + + scope->provide("A"); + mgr->start("t1", "t1", {}); + val_equal(x, "1") a; # must have bound to A immediately + assert(a); + + scope->provide("B") mgr; + val_equal(x, "2") a; # must have rebound to B immediately + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template t1 { + _caller.scope->depend({"B", "A"}) dep; + num_add(dep.x, "1") new_x; + dep.x->set(new_x); +} diff --git a/external/badvpn_dns/ncd/tests/escape_and_nulls.ncd b/external/badvpn_dns/ncd/tests/escape_and_nulls.ncd new file mode 100644 index 00000000..741bf8d0 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/escape_and_nulls.ncd @@ -0,0 +1,38 @@ +process main { + value("ab\0") str1; + value("ab") str2; + + strcmp(str1.length, "3") a; + assert(a); + + strcmp(str2.length, "2") a; + assert(a); + + strcmp(str1, str2) a; + not(a) a; + assert(a); + + concat(str1, str2) strc; + strcmp(strc, "ab\0ab") a; + assert(a); + + concat(str2, str1) strc; + strcmp(strc, "abab\0") a; + assert(a); + + value("") str1; + value("\x00\x00") str2; + value("\x00\x01") str3; + value("\x01") str4; + + val_lesser(str1, str2) a; + assert(a); + + val_lesser(str2, str3) a; + assert(a); + + val_lesser(str3, str4) a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/explode.ncd b/external/badvpn_dns/ncd/tests/explode.ncd new file mode 100644 index 00000000..20913a8f --- /dev/null +++ b/external/badvpn_dns/ncd/tests/explode.ncd @@ -0,0 +1,23 @@ +process main { + explode("FOO", "aaaFOObbbFOOcccFOOddd") l; + val_equal(l, {"aaa", "bbb", "ccc", "ddd"}) a; + assert(a); + + explode("FOO", "FOObbbFOOFOO") l; + val_equal(l, {"", "bbb", "", ""}) a; + assert(a); + + explode("FOO", "foo") l; + val_equal(l, {"foo"}) a; + assert(a); + + explode("FOO", "FOO") l; + val_equal(l, {"", ""}) a; + assert(a); + + explode("participate in parachute", "parachute in participation of participate in parachuteparparticipate in parachute participate in parachut") l; + val_equal(l, {"parachute in participation of ", "par", " participate in parachut"}) a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/foreach.ncd b/external/badvpn_dns/ncd/tests/foreach.ncd new file mode 100644 index 00000000..33ad042f --- /dev/null +++ b/external/badvpn_dns/ncd/tests/foreach.ncd @@ -0,0 +1,35 @@ +process main { + var({"a", "b", "c", "d"}) list; + value(["a":"1", "b":"2", "c":"3", "d":"4"]) map; + + value({}) new; + Foreach (list As value) { + new->insert(new.length, value); + }; + val_equal(new, list) a; + assert(a); + + value({}) new; + Foreach (list As index:value) { + new->insert(index, value); + }; + val_equal(new, list) a; + assert(a); + + value([]) new; + Foreach (map As key) { + map->get(key) value; + new->insert(key, value); + }; + val_equal(new, map) a; + assert(a); + + value([]) new; + Foreach (map As key:value) { + new->insert(key, value); + }; + val_equal(new, map) a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/if.ncd b/external/badvpn_dns/ncd/tests/if.ncd new file mode 100644 index 00000000..4597adc4 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/if.ncd @@ -0,0 +1,38 @@ +process foo { + If ("true") { + If ("truee") { + var("A1") y; + } else { + If ("true") { + var("A11") q; + } else { + var("A22") q; + } t; + var(t.q) y; + } s; + var(s.y) x; + } elif ("true") { + var("B") x; + } else { + var("C") x; + } ifs; + + val_equal(ifs.x, "A11") a; + assert(a); + + var("a") v; + If ("false") { + v->set("b"); + }; + val_equal(v, "a") a; + assert(a); + + var("a") v; + If ("true") { + v->set("b"); + }; + val_equal(v, "b") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/implode.ncd b/external/badvpn_dns/ncd/tests/implode.ncd new file mode 100644 index 00000000..2197c177 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/implode.ncd @@ -0,0 +1,15 @@ +process main { + implode("X", {"a", "bb", "", "c"}) str; + strcmp(str, "aXbbXXc") a; + assert(a); + + implode("", {"a", "b"}) str; + strcmp(str, "ab") a; + assert(a); + + implode("X", {}) str; + strcmp(str, "") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/include.ncd b/external/badvpn_dns/ncd/tests/include.ncd new file mode 100644 index 00000000..eccb76d3 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/include.ncd @@ -0,0 +1,16 @@ +include "include_included.ncdi" +include "include_included.ncdi" +include "include_included2.ncdi" +include "include_included2.ncdi" + +process main { + call("incl_tmpl", {}) c; + val_equal(c.x, "good") a; + assert(a); + + call("incl_tmpl2", {}) c; + val_equal(c.x, "good2") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/include_included.ncdi b/external/badvpn_dns/ncd/tests/include_included.ncdi new file mode 100644 index 00000000..5fbfd622 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/include_included.ncdi @@ -0,0 +1,5 @@ +include_guard "include_included" + +template incl_tmpl { + var("good") x; +} diff --git a/external/badvpn_dns/ncd/tests/include_included2.ncdi b/external/badvpn_dns/ncd/tests/include_included2.ncdi new file mode 100644 index 00000000..d7653015 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/include_included2.ncdi @@ -0,0 +1,5 @@ +include_guard "include_included2" + +template incl_tmpl2 { + var("good2") x; +} diff --git a/external/badvpn_dns/ncd/tests/logical.ncd b/external/badvpn_dns/ncd/tests/logical.ncd new file mode 100644 index 00000000..e8956ec8 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/logical.ncd @@ -0,0 +1,46 @@ +process main { + var("true") t; + var("Faalse") f; + + and(t, f) r; + strcmp(r, "false") a; + assert(a); + + and(f, t) r; + strcmp(r, "false") a; + assert(a); + + and(f, f) r; + strcmp(r, "false") a; + assert(a); + + and(t, t) r; + strcmp(r, "true") a; + assert(a); + + or(t, f) r; + strcmp(r, "true") a; + assert(a); + + or(f, t) r; + strcmp(r, "true") a; + assert(a); + + or(t, t) r; + strcmp(r, "true") a; + assert(a); + + or(f, f) r; + strcmp(r, "false") a; + assert(a); + + not(f) r; + strcmp(r, "true") a; + assert(a); + + not(t) r; + strcmp(r, "false") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/multidepend.ncd b/external/badvpn_dns/ncd/tests/multidepend.ncd new file mode 100644 index 00000000..b641f39a --- /dev/null +++ b/external/badvpn_dns/ncd/tests/multidepend.ncd @@ -0,0 +1,30 @@ +process main { + var("0") x; + process_manager() mgr; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "2") a; # must not have rebound temporarily to A during backtracking + assert(a); + exit("0"); + }; + + multiprovide("A"); + mgr->start("t1", "t1", {}); + val_equal(x, "1") a; # must have bound to A immediately + assert(a); + + multiprovide("B") mgr; + val_equal(x, "2") a; # must have rebound to B immediately + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template t1 { + multidepend({"B", "A"}) dep; + num_add(dep.x, "1") new_x; + dep.x->set(new_x); +} diff --git a/external/badvpn_dns/ncd/tests/netmask.ncd b/external/badvpn_dns/ncd/tests/netmask.ncd new file mode 100644 index 00000000..90cd7443 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/netmask.ncd @@ -0,0 +1,15 @@ +process main { + ipv4_prefix_to_mask("16") mask; + strcmp(mask, "255.255.0.0") a; + assert(a); + + ipv4_mask_to_prefix("128.0.0.0") prefix; + strcmp(prefix, "1") a; + assert(a); + + ipv4_net_from_addr_and_prefix("192.168.1.4", "24") net; + strcmp(net, "192.168.1.0") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/parse.ncd b/external/badvpn_dns/ncd/tests/parse.ncd new file mode 100644 index 00000000..ae4820c4 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/parse.ncd @@ -0,0 +1,85 @@ +process main { + parse_number("awfa") x; + not(x.succeeded) a; + assert(a); + + parse_number("023182") x; + assert(x.succeeded); + val_equal(x, "23182") a; + assert(a); + + parse_ipv4_addr("192.168.61.007") x; + assert(x.succeeded); + val_equal(x, "192.168.61.7") a; + assert(a); + + parse_ipv6_addr("1234:0000::abcd") x; + assert(x.succeeded); + val_equal(x, "1234::abcd") a; + assert(a); + + parse_value("{\"Hello World\", {}}") x; + assert(x.succeeded); + val_equal(x, {"Hello World", {}}) a; + assert(a); + + var({"Hello", "fw", {}, {}, ["key":{{}}, [[]:[]]:["k":"v"]], {"st", {"ri", {"ng", [[{}:{}]:[]]}}}}) v; + to_string(v) str; + from_string(str) v2; + to_string(v2) str2; + val_equal(v, v2) a; + assert(a); + val_equal(str, str2) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}") x; + assert(x.succeeded); + + parse_value("{\"Hello\", \"fw\", {}, {}, \"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st, {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", \"ri\", \"ng\", [[{}:{}]:[]]}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{\"Hello\", \"fw\", {}, {}, [\"key\":{{}}, [[]:[]]:[\"k\":\"v\"]], {\"st\", {\"ri\", {\"ng\", [[{}:{}]:[]]}}}") x; + not(x.succeeded) a; + assert(a); + + parse_value("{syntax error") x; + not(x.succeeded) a; + assert(a); + + parse_ipv4_cidr_addr("192.168.61.007/24") x; + assert(x.succeeded); + val_equal(x, "192.168.61.7/24") a; + assert(a); + val_equal(x.addr, "192.168.61.7") a; + assert(a); + val_equal(x.prefix, "24") a; + assert(a); + + parse_ipv4_cidr_addr("192.168.61.007/33") x; + not(x.succeeded) a; + assert(a); + + parse_ipv6_cidr_addr("1234:0000::abcd/41") x; + assert(x.succeeded); + val_equal(x, "1234::abcd/41") a; + assert(a); + val_equal(x.addr, "1234::abcd") a; + assert(a); + val_equal(x.prefix, "41") a; + assert(a); + + parse_ipv6_cidr_addr("1234:0000::abcd/129") x; + not(x.succeeded) a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/process_manager.ncd b/external/badvpn_dns/ncd/tests/process_manager.ncd new file mode 100644 index 00000000..98f0819e --- /dev/null +++ b/external/badvpn_dns/ncd/tests/process_manager.ncd @@ -0,0 +1,112 @@ +process main { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + call("phase2", {}); + }; + + process_manager() mgr; + + mgr->start("name", "increment", {"1", "2", "false"}); + val_equal(x, "1") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "3") a; + assert(a); + + mgr->start("name", "increment", {"3", "4", "true"}); + val_equal(x, "6") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "6") a; + assert(a); + + mgr->start("name", "increment", {"5", "6", "false"}); + val_equal(x, "6") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template phase2 { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + call("phase3", {}); + }; + + process_manager() mgr; + + mgr->start("name", "increment", {"1", "2", "true"}); + val_equal(x, "1") a; + assert(a); + + mgr->stop("name"); + val_equal(x, "1") a; + assert(a); + + mgr->start("name", "increment", {"3", "4", "true"}); + val_equal(x, "1") a; + assert(a); + + depend("INC_DONE"); + val_equal(x, "6") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template phase3 { + var("0") x; + + var("false") backtrack_check; + backtrack_point() point; + If (backtrack_check) { + val_equal(x, "10") a; + assert(a); + exit("0"); + }; + + process_manager() mgr; + + mgr->start("increment", {"1", "2", "false"}); + val_equal(x, "1") a; + assert(a); + + mgr->start("increment", {"3", "4", "false"}); + val_equal(x, "4") a; + assert(a); + + backtrack_check->set("true"); + point->go(); +} + +template increment { + var(_arg0) amount; + var(_arg1) amount_deinit; + var(_arg2) do_sleep; + imperative("", {}, "increment_deinit_inc", {}, "10000"); + num_add(_caller.x, amount) new_x; + _caller.x->set(new_x); + If (do_sleep) { + sleep("0", "0"); + }; + provide("INC_DONE"); +} + +template increment_deinit_inc { + num_add(_caller._caller.x, _caller.amount_deinit) new_x; + _caller._caller.x->set(new_x); +} diff --git a/external/badvpn_dns/ncd/tests/regex.ncd b/external/badvpn_dns/ncd/tests/regex.ncd new file mode 100644 index 00000000..3175ebe9 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/regex.ncd @@ -0,0 +1,48 @@ +process main { + var("FOO BAR BAZ QUX goo") x; + regex_replace(x, {"FOO", "BAR", "goo"}, {"BAR", "bar", "GOO"}) y; + strcmp(y, "BAR bar BAZ QUX GOO") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"^hello"}, {"Hello,"}) y; + strcmp(y, "Hello, world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"goodbye"}, {"hello"}) y; + strcmp(y, "hello world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"hello world"}, {"hello NCD"}) y; + strcmp(y, "hello NCD") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"wor"}, {"Wor"}) y; + strcmp(y, "hello World") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "llo"}, {"ELL", "LLO"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "el"}, {"ELL", "EL"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"el", "lo"}, {"EL", "LO"}) y; + strcmp(y, "hELLO world") a; + assert(a); + + var("hello world") x; + regex_replace(x, {"ell", "ll"}, {"ELL", "LL"}) y; + strcmp(y, "hELLo world") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/ncd/tests/run_tests b/external/badvpn_dns/ncd/tests/run_tests new file mode 100755 index 00000000..071fd20f --- /dev/null +++ b/external/badvpn_dns/ncd/tests/run_tests @@ -0,0 +1,38 @@ +#!/bin/bash + +NCD=$1 +USE_VALGRIND=$2 + +if [[ -z $NCD ]] || [[ -n $USE_VALGRIND && $USE_VALGRIND != use_valgrind ]]; then + echo "Usage: $0 [use_valgrind]" + exit 1 +fi + +if [[ ! -e ./run_tests ]]; then + echo "Must run from the tests directory" + exit 1 +fi + +failed=0 + +for file in ./*.ncd; do + echo "Running: $file" + if [[ $USE_VALGRIND = use_valgrind ]]; then + valgrind --error-exitcode=1 --leak-check=full "$NCD" --loglevel none --config-file "$file" + else + "$NCD" --loglevel none --config-file "$file" + fi + res=$? + if [[ ! $res -eq 0 ]]; then + echo "FAILED" + let failed+=1 + fi +done + +if [[ $failed -gt 0 ]]; then + echo "$failed tests FAILED" + exit 1 +fi + +echo "all tests passed" +exit 0 diff --git a/external/badvpn_dns/ncd/tests/strings.ncd b/external/badvpn_dns/ncd/tests/strings.ncd new file mode 100644 index 00000000..7be031be --- /dev/null +++ b/external/badvpn_dns/ncd/tests/strings.ncd @@ -0,0 +1,47 @@ +process main { + buffer() buf; + buf->append("12"); + buf->append("345"); + buf->append("6"); + num_equal(buf, "123456") a; + assert(a); + + var("false") check; + call("test_func", {}); + assert(check); + + buffer() buf; + buf->append("test_func"); + var("false") check; + call(buf, {}); + assert(check); + + concat("test_func") cnc; + var("false") check; + call(cnc, {}); + assert(check); + + buffer() buf; + buf->append("test_func"); + var("false") check; + process_manager() mgr; + mgr->start(buf, {}); + assert(check); + + buffer() buf; + buf->append("/bin/echo"); + runonce({buf, buf}); + + buffer() buf; + buf->append("12"); + buf->append("345"); + to_string(buf) str; + val_equal(str, "\"12345\"") a; + assert(a); + + exit("0"); +} + +template test_func { + _caller.check->set("true"); +} diff --git a/external/badvpn_dns/ncd/tests/substr.ncd b/external/badvpn_dns/ncd/tests/substr.ncd new file mode 100644 index 00000000..a3b07b1f --- /dev/null +++ b/external/badvpn_dns/ncd/tests/substr.ncd @@ -0,0 +1,37 @@ +process main { + var("0123456789") str; + concat(str) external_str; + + call("do_test", {"_caller.str"}); + call("do_test", {"_caller.external_str"}); + + exit("0"); +} + +template do_test { + alias(_arg0) str; + + substr(str, "0") sub; + val_equal(sub, "0123456789") a; + assert(a); + + substr(str, "2") sub; + val_equal(sub, "23456789") a; + assert(a); + + substr(str, "3", "0") sub; + val_equal(sub, "") a; + assert(a); + + substr(str, "3", "6") sub; + val_equal(sub, "345678") a; + assert(a); + + substr(str, "3", "7") sub; + val_equal(sub, "3456789") a; + assert(a); + + substr(str, "3", "8") sub; + val_equal(sub, "3456789") a; + assert(a); +} diff --git a/external/badvpn_dns/ncd/tests/turing.ncd b/external/badvpn_dns/ncd/tests/turing.ncd new file mode 100644 index 00000000..4e764f18 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/turing.ncd @@ -0,0 +1,138 @@ +process main { + # Turing machine specification. + var("B") blank; + var([ + {"0", "0"}:{"0", "0", "right"}, + {"0", "1"}:{"1", "x", "right"}, + {"1", "1"}:{"1", "1", "right"}, + {"1", "0"}:{"2", "0", "right"}, + {"2", "0"}:{"2", "0", "right"}, + {"2", "1"}:{"3", "1", "right"}, + {"3", "1"}:{"3", "1", "right"}, + {"3", "0"}:{"4", "1", "left"}, + {"3", "B"}:{"4", "1", "left"}, + {"4", "1"}:{"4", "1", "left"}, + {"4", "0"}:{"5", "0", "left"}, + {"5", "0"}:{"5", "0", "left"}, + {"5", "1"}:{"6", "1", "left"}, + {"5", "x"}:{"h", "x", "stay"}, + {"6", "1"}:{"6", "1", "left"}, + {"6", "x"}:{"0", "x", "right"}, + {"6", "0"}:{"0", "0", "right"} + ]) rules; + var("0") initial_state; + var({}) initial_tape_left; + var({ + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "0", "0", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1" + }) initial_tape_right; + + # Perform the computation, stopping when no rule matches. + call("turing", {blank, rules, initial_state, initial_tape_left, initial_tape_right}) results; + + # Check results. + + val_equal(results.tape_left, {"B"}) a; + assert(a); + + val_equal(results.tape_right, + {"x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", + "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", + "0", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "1"} + ) a; + assert(a); + + val_equal({results.side, results.pos}, {"right", "55"}) a; + assert(a); + + val_equal(results.state, "h") a; + assert(a); + + exit("0"); +} + +template turing { + alias("_arg0") blank; + value(_arg1) rules; + alias("_arg2") initial_state; + alias("_arg3") initial_tape_left; + alias("_arg4") initial_tape_right; + + # Head state. + var(initial_state) state; + + # Tape. Positions go like this: ... L2 L1 L0 R0 R1 R2 ... + value(initial_tape_left) tape_left; + value(initial_tape_right) tape_right; + + # Make sure each side of the tape has at least one symbol so we can flip easily. + tape_left->insert(tape_left.length, blank); + tape_right->insert(tape_right.length, blank); + + # Head position. + var("right") side; + var("0") pos; + + # Enter loop. + blocker() loop_blk; + loop_blk->up(); + loop_blk->use(); + + # Get symbol under head. + concat("tape_", side) tape_name; + alias(tape_name) cur_tape; + cur_tape->get(pos) symbol; + + # Look for a matching rule. + rules->try_get({state, symbol}) rule; + + If (rule.exists) { + # Extract directions from rule. + rule->get("0") new_state; + rule->get("1") new_symbol; + rule->get("2") move; + + # Change head state. + state->set(new_state); + + # Replace symbol under head. + cur_tape->remove(pos); + cur_tape->insert(pos, new_symbol); + + # Branch based on how we move. + strcmp(move, side) is_outside; + strcmp(move, "stay") is_stay; + strcmp(pos, "0") is_zero; + If (is_outside) { + # Increment position. + num_add(pos, "1") new_pos; + pos->set(new_pos); + + # If the new position is out of range, extend tape. + strcmp(pos, cur_tape.length) need_extend; + If (need_extend) { + cur_tape->insert(pos, blank); + }; + } elif (is_stay) { + # Nop. + getargs(); + } elif (is_zero) { + # Flip side, leave pos at zero. + side->set(move); + } else { + # Decrement position. + num_subtract(pos, "1") new_pos; + pos->set(new_pos); + }; + + # Continue loop. + loop_blk->downup(); + }; +} diff --git a/external/badvpn_dns/ncd/tests/value.ncd b/external/badvpn_dns/ncd/tests/value.ncd new file mode 100644 index 00000000..e483b3f2 --- /dev/null +++ b/external/badvpn_dns/ncd/tests/value.ncd @@ -0,0 +1,258 @@ +process main { + value({"A", {"B", "C"}, {{"D"}, "E"}}) v; + val_equal(v, {"A", {"B", "C"}, {{"D"}, "E"}}) a; + assert(a); + + v->get("1") w; + val_equal(w, {"B", "C"}) a; + assert(a); + + w->delete(); + val_equal(v, {"A", {{"D"}, "E"}}) a; + assert(a); + + v->getpath({"1", "1"}) f; + val_equal(f, "E") a; + assert(a); + + value(["hello":{"Hello", "Good evening!"}, "goodbye":{"Bye", "See you"}]) v; + val_equal(v, ["hello":{"Hello", "Good evening!"}, "goodbye":{"Bye", "See you"}]) a; + assert(a); + + val_equal(v.keys, {"goodbye", "hello"}) a; + assert(a); + + v->get("hello") h; + val_equal(h, {"Hello", "Good evening!"}) a; + assert(a); + + v->get("goodbye") g; + val_equal(g, {"Bye", "See you"}) a; + assert(a); + + g->delete(); + val_equal(v, ["hello":{"Hello", "Good evening!"}]) a; + assert(a); + + h->delete(); + val_equal(v, []); + assert(a); + + v->delete(); + strcmp(v.exists, "false") a; + assert(a); + + value({"D", "F", "H"}) v; + v->insert("0", "A") a; # ADFH + v->insert("1", "B") b; # ABDFH + v->insert("5", "I") i; # ABDFHI + val_equal(v, {"A", "B", "D", "F", "H", "I"}) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->insert("k0", "v0") v0; # k0=v0 k1=v1 k2=v2 k3=v3 + v->insert("k0", "V0") V0; # k0=V0 k1=v1 k2=v2 k3=v3 + val_equal(v0, "v0") a; + assert(a); + val_equal(V0, "V0") a; + assert(a); + + value({"D", "F", "H"}) v; + v->remove("0"); # FH + v->remove("1"); # F + val_equal(v, {"F"}) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->remove("k1"); + v->remove("k3"); + val_equal(v, ["k2":"v2"]) a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + v->try_get("k1") v1; + v->try_get("k7") v7; + val_equal(v1.exists, "true") a; + assert(a); + val_equal(v7.exists, "false") a; + assert(a); + + value(["k1":"v1", "k2":"v2", "k3":"v3"]) v; + imperative("", {}, "check1", {}, "10000"); + v->insert_undo("k1", "V1") V1; + strcmp(V1, "V1") a; + assert(a); + v->insert_undo("k4", "V4"); + v->remove("k2"); + val_equal(v, ["k1":"V1", "k3":"v3", "k4":"V4"]) a; + assert(a); + + value({"a", "b", "c"}) v; + v->replace("0", "A"); + v->replace("1", "B"); + v->replace("2", "C"); + v->replace("3", "D"); + val_equal(v, {"A", "B", "C", "D"}) a; + assert(a); + + value({"a", "b", "c"}) v; + imperative("", {}, "check2", {}, "10000"); + v->replace_undo("0", "A"); + v->replace_undo("1", "B"); + v->replace_undo("2", "C"); + v->replace_undo("3", "D"); + val_equal(v, {"A", "B", "C", "D"}) a; + assert(a); + + value("A") v; + v->reset("B"); + val_equal(v, "B") a; + assert(a); + + value({"a", "c"}) v; + v->insert_undo("1", "b") vb; + val_equal(v, {"a", "b", "c"}) a; + assert(a); + val_equal(vb, "b") a; + assert(a); + vb->reset("B"); + val_equal(v, {"a", "c"}) a; + assert(a); + val_equal(vb, "B") a; + assert(a); + + value({"a", "b", "c"}) v; + v->get("1") vb; + vb->replace_this("B") vB; + val_equal(vb, "b") a; + assert(a); + val_equal(vB, "B") a; + assert(a); + v->get("1") vB2; + val_equal(vB2, "B") a; + assert(a); + + value(["a":"va", "b":"vb", "c":"vc"]) v; + v->get("b") vb; + vb->replace_this("vB") vB; + val_equal(vB, "vB") a; + assert(a); + val_equal(vb, "vb") a; + assert(a); + v->get("b") vB2; + val_equal(vB2, "vB") a; + assert(a); + + value({"a", "b", "c"}) v; + v->get("1") vb; + imperative("", {}, "check3", {}, "10000"); + vb->replace_this_undo("B") vB; + val_equal(vb, "b") a; + assert(a); + val_equal(vB, "B") a; + assert(a); + v->get("1") vB2; + val_equal(vB2, "B") a; + assert(a); + + value(["a":"va", "b":"vb", "c":"vc"]) v; + v->get("b") vb; + imperative("", {}, "check4", {}, "10000"); + vb->replace_this_undo("vB") vB; + val_equal(vB, "vB") a; + assert(a); + val_equal(vb, "vb") a; + assert(a); + v->get("b") vB2; + val_equal(vB2, "vB") a; + assert(a); + + value("") v; + v->append("ab"); + v->append("cde"); + v->append(v); + v->append(""); + v->append("f"); + val_equal(v, "abcdeabcdef") a; + assert(a); + + value({}) v; + v->insert("1") ins_v; + v->insert("2"); + v->insert("3"); + v->insert("4"); + v->append("5"); + v->append("6"); + val_equal(v, {"1", "2", "3", "4", "5", "6"}) a; + assert(a); + val_equal(ins_v, "1") a; + assert(a); + + value({}) v; + imperative("", {}, "check5", {}, "10000"); + v->insert_undo("1"); + v->insert_undo("2"); + v->insert_undo("3"); + v->insert_undo("4"); + val_equal(v, {"1", "2", "3", "4"}) a; + assert(a); + + buffer() buf; + buf->append("123"); + value(buf) v; + val_equal(v, "123") a; + assert(a); + v->substr("2") sub_v; + val_equal(sub_v, "3") a; + assert(a); + v->append("456789012345"); + val_equal(v, "123456789012345") a; + assert(a); + buffer() numbuf; + numbuf->append("1"); + numbuf->append("2"); + v->substr(numbuf) sub_v; + val_equal(sub_v, "345") a; + assert(a); + + concat("hello", "world") cnc; + value(cnc) v; + val_equal(v, "helloworld") a; + assert(a); + v->substr("2") sub_v; + val_equal(sub_v, "lloworld") a; + assert(a); + v->append("!!"); + val_equal(v, "helloworld!!") a; + assert(a); + v->substr("1") sub_v; + val_equal(sub_v, "elloworld!!") a; + assert(a); + + exit("0"); +} + +template check1 { + val_equal(_caller.v, ["k1":"v1", "k3":"v3"]) a; + assert(a); +} + +template check2 { + val_equal(_caller.v, {"a", "b", "c"}) a; + assert(a); +} + +template check3 { + val_equal(_caller.v, {"a", "b", "c"}) a; + assert(a); +} + +template check4 { + val_equal(_caller.v, ["a":"va", "b":"vb", "c":"vc"]) a; + assert(a); +} + +template check5 { + val_equal(_caller.v, {}) a; + assert(a); +} diff --git a/external/badvpn_dns/ncd/tests/value_substr.ncd b/external/badvpn_dns/ncd/tests/value_substr.ncd new file mode 100644 index 00000000..ea46e8ab --- /dev/null +++ b/external/badvpn_dns/ncd/tests/value_substr.ncd @@ -0,0 +1,25 @@ +process foo { + value("0123456789") str; + + str->substr("0") sub; + strcmp(sub, str) a; + assert(a); + + str->substr("1") sub; + strcmp(sub, "123456789") a; + assert(a); + + str->substr("1", "0") sub; + strcmp(sub, "") a; + assert(a); + + str->substr("1", "9") sub; + strcmp(sub, "123456789") a; + assert(a); + + str->substr("1", "8") sub; + strcmp(sub, "12345678") a; + assert(a); + + exit("0"); +} diff --git a/external/badvpn_dns/nspr_support/BSSLConnection.c b/external/badvpn_dns/nspr_support/BSSLConnection.c new file mode 100644 index 00000000..fea9c948 --- /dev/null +++ b/external/badvpn_dns/nspr_support/BSSLConnection.c @@ -0,0 +1,1024 @@ +/** + * @file BSSLConnection.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include +#include + +#include "BSSLConnection.h" + +#include + +#define THREADWORK_STATE_NONE 0 +#define THREADWORK_STATE_HANDSHAKE 1 +#define THREADWORK_STATE_READ 2 +#define THREADWORK_STATE_WRITE 3 + +static void backend_threadwork_start (struct BSSLConnection_backend *b, int op); +static int backend_threadwork_do_io (struct BSSLConnection_backend *b); +static void connection_init_job_handler (BSSLConnection *o); +static void connection_init_up (BSSLConnection *o); +static void connection_try_io (BSSLConnection *o); +static void connection_threadwork_func_work (void *user); +static void connection_threadwork_handler_done (void *user); +static void connection_recv_job_handler (BSSLConnection *o); +static void connection_try_handshake (BSSLConnection *o); +static void connection_try_send (BSSLConnection *o); +static void connection_try_recv (BSSLConnection *o); +static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len); +static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len); + +int bprconnection_initialized = 0; +PRDescIdentity bprconnection_identity; + +static PRFileDesc * get_bottom (PRFileDesc *layer) +{ + while (layer->lower) { + layer = layer->lower; + } + + return layer; +} + +static PRStatus method_close (PRFileDesc *fd) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(!b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + + // free mutexes + if ((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + BMutex_Free(&b->recv_buf_mutex); + BMutex_Free(&b->send_buf_mutex); + } + + // free backend + free(b); + + // set no secret + fd->secret = NULL; + + return PR_SUCCESS; +} + +static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(amount > 0) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->recv_buf_mutex); + } + + // if we are receiving into buffer or buffer has no data left, refuse recv + if (b->recv_busy || b->recv_pos == b->recv_len) { + if (b->threadwork_state != THREADWORK_STATE_NONE) { + b->threadwork_want_recv = 1; + BMutex_Unlock(&b->recv_buf_mutex); + } else { + // start receiving if not already + if (!b->recv_busy) { + // set recv busy + b->recv_busy = 1; + + // receive into buffer + StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); + } + } + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + // limit amount to available data + if (amount > b->recv_len - b->recv_pos) { + amount = b->recv_len - b->recv_pos; + } + + // copy data + memcpy(buf, b->recv_buf + b->recv_pos, amount); + + // update buffer + b->recv_pos += amount; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->recv_buf_mutex); + } + + return amount; +} + +static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret; + ASSERT(amount > 0) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->send_buf_mutex); + } + + ASSERT(!b->send_busy || b->send_pos < b->send_len) + + // if there is data in buffer, refuse send + if (b->send_pos < b->send_len) { + if (b->threadwork_state != THREADWORK_STATE_NONE) { + b->threadwork_want_send = 1; + BMutex_Unlock(&b->send_buf_mutex); + } + PR_SetError(PR_WOULD_BLOCK_ERROR, 0); + return -1; + } + + // limit amount to buffer size + if (amount > BSSLCONNECTION_BUF_SIZE) { + amount = BSSLCONNECTION_BUF_SIZE; + } + + // init buffer + memcpy(b->send_buf, buf, amount); + b->send_pos = 0; + b->send_len = amount; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } else { + // start sending + b->send_busy = 1; + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + } + + return amount; +} + +static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how) +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + ASSERT(flags == 0) + + return method_read(fd, buf, amount); +} + +static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + ASSERT(flags == 0) + + return method_write(fd, buf, amount); +} + +static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + *out_flags = 0; + return in_flags; +} + +static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + memset(addr, 0, sizeof(*addr)); + addr->raw.family = PR_AF_INET; + return PR_SUCCESS; +} + +static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data) +{ + switch (data->option) { + case PR_SockOpt_Nonblocking: + data->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + } + + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data) +{ + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRIntn _PR_InvalidIntn (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt32 _PR_InvalidInt32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt64 _PR_InvalidInt64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset32 _PR_InvalidOffset32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset64 _PR_InvalidOffset64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRStatus _PR_InvalidStatus (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc *_PR_InvalidDesc (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +static PRIOMethods methods = { + (PRDescType)0, + method_close, + method_read, + method_write, + (PRAvailableFN)_PR_InvalidInt32, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidOffset32, + (PRSeek64FN)_PR_InvalidOffset64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt32, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + method_shutdown, + method_recv, + method_send, + (PRRecvfromFN)_PR_InvalidInt32, + (PRSendtoFN)_PR_InvalidInt32, + method_poll, + (PRAcceptreadFN)_PR_InvalidInt32, + (PRTransmitfileFN)_PR_InvalidInt32, + (PRGetsocknameFN)_PR_InvalidStatus, + method_getpeername, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + method_getsocketoption, + method_setsocketoption, + (PRSendfileFN)_PR_InvalidInt32, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn +}; + +static void backend_send_if_handler_done (struct BSSLConnection_backend *b, int data_len) +{ + ASSERT(b->send_busy) + ASSERT(b->send_len > 0) + ASSERT(b->send_pos < b->send_len) + ASSERT(data_len > 0) + ASSERT(data_len <= b->send_len - b->send_pos) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->send_buf_mutex); + } + + // update buffer + b->send_pos += data_len; + + // send more if needed + if (b->send_pos < b->send_len) { + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } + return; + } + + // set send not busy + b->send_busy = 0; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->send_buf_mutex); + } + + // notify connection + if (b->con && !b->con->have_error) { + connection_try_io(b->con); + return; + } +} + +static void backend_recv_if_handler_done (struct BSSLConnection_backend *b, int data_len) +{ + ASSERT(b->recv_busy) + ASSERT(data_len > 0) + ASSERT(data_len <= BSSLCONNECTION_BUF_SIZE) + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Lock(&b->recv_buf_mutex); + } + + // init buffer + b->recv_busy = 0; + b->recv_pos = 0; + b->recv_len = data_len; + + if (b->threadwork_state != THREADWORK_STATE_NONE) { + BMutex_Unlock(&b->recv_buf_mutex); + } + + // notify connection + if (b->con && !b->con->have_error) { + connection_try_io(b->con); + return; + } +} + +static void backend_threadwork_start (struct BSSLConnection_backend *b, int op) +{ + ASSERT(b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + ASSERT(op == THREADWORK_STATE_HANDSHAKE || op == THREADWORK_STATE_READ || op == THREADWORK_STATE_WRITE) + + b->threadwork_state = op; + b->threadwork_want_recv = 0; + b->threadwork_want_send = 0; + BThreadWork_Init(&b->threadwork, b->twd, connection_threadwork_handler_done, b->con, connection_threadwork_func_work, b->con); +} + +static int backend_threadwork_do_io (struct BSSLConnection_backend *b) +{ + ASSERT(b->con) + ASSERT(b->threadwork_state == THREADWORK_STATE_NONE) + + int io_ready = (b->threadwork_want_recv && !b->recv_busy && b->recv_pos < b->recv_len) || + (b->threadwork_want_send && b->send_pos == b->send_len); + + if (b->threadwork_want_recv && b->recv_pos == b->recv_len && !b->recv_busy) { + b->recv_busy = 1; + StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE); + } + + if (b->send_pos < b->send_len && !b->send_busy) { + b->send_busy = 1; + StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos); + } + + return io_ready; +} + +static void connection_report_error (BSSLConnection *o) +{ + ASSERT(!o->have_error) + + // set error + o->have_error = 1; + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BSSLCONNECTION_EVENT_ERROR)); +} + +static void connection_init_job_handler (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(!o->up) + + connection_try_handshake(o); +} + +static void connection_init_up (BSSLConnection *o) +{ + // unset init job + // (just in the impossible case that handshake completed before the init job executed) + BPending_Unset(&o->init_job); + + // init send interface + StreamPassInterface_Init(&o->send_if, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, o->pg); + + // init recv interface + StreamRecvInterface_Init(&o->recv_if, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, o->pg); + + // init recv job + BPending_Init(&o->recv_job, o->pg, (BPending_handler)connection_recv_job_handler, o); + + // set no send data + o->send_len = -1; + + // set no recv data + o->recv_avail = -1; + + // set up + o->up = 1; +} + +static void connection_try_io (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + + if (!o->up) { + connection_try_handshake(o); + return; + } + + if (o->send_len > 0) { + if (o->recv_avail > 0) { + BPending_Set(&o->recv_job); + } + + connection_try_send(o); + return; + } + + if (o->recv_avail > 0) { + connection_try_recv(o); + return; + } +} + +static void connection_threadwork_func_work (void *user) +{ + BSSLConnection *o = (BSSLConnection *)user; + struct BSSLConnection_backend *b = o->backend; + ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) + + switch (b->threadwork_state) { + case THREADWORK_STATE_HANDSHAKE: + b->threadwork_result_sec = SSL_ForceHandshake(o->prfd); + break; + case THREADWORK_STATE_WRITE: + b->threadwork_result_pr = PR_Write(o->prfd, o->send_data, o->send_len); + break; + case THREADWORK_STATE_READ: + b->threadwork_result_pr = PR_Read(o->prfd, o->recv_data, o->recv_avail); + break; + default: + ASSERT(0); + } + + b->threadwork_error = PR_GetError(); +} + +static void connection_threadwork_handler_done (void *user) +{ + BSSLConnection *o = (BSSLConnection *)user; + struct BSSLConnection_backend *b = o->backend; + ASSERT(b->threadwork_state != THREADWORK_STATE_NONE) + + // remember what operation the threadwork was performing + int op = b->threadwork_state; + + // free threadwork + BThreadWork_Free(&b->threadwork); + b->threadwork_state = THREADWORK_STATE_NONE; + + // start any necessary backend I/O operations, and determine if any of the requested + // backend I/O that was not available at the time is now available + int io_ready = backend_threadwork_do_io(b); + + switch (op) { + case THREADWORK_STATE_HANDSHAKE: { + ASSERT(!o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) + + if (b->threadwork_result_sec == SECFailure) { + if (b->threadwork_error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); + } + return; + } + BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", b->threadwork_error); + connection_report_error(o); + return; + } + + // init up + connection_init_up(o); + + // report up + o->handler(o->user, BSSLCONNECTION_EVENT_UP); + return; + } break; + + case THREADWORK_STATE_WRITE: { + ASSERT(o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) + ASSERT(o->send_len > 0) + + PRInt32 result = b->threadwork_result_pr; + PRErrorCode error = b->threadwork_error; + + if (result < 0) { + if (error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } else if (o->recv_avail > 0) { + // don't forget about receiving + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + return; + } + BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + ASSERT(result > 0) + ASSERT(result <= o->send_len) + + // set no send data + o->send_len = -1; + + // don't forget about receiving + if (o->recv_avail > 0) { + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + + // finish send operation + StreamPassInterface_Done(&o->send_if, result); + } break; + + case THREADWORK_STATE_READ: { + ASSERT(o->up) + ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) + ASSERT(o->recv_avail > 0) + + PRInt32 result = b->threadwork_result_pr; + PRErrorCode error = b->threadwork_error; + + if (result < 0) { + if (error == PR_WOULD_BLOCK_ERROR) { + if (io_ready) { + // requested backend I/O got ready, try again + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } else if (o->send_len > 0) { + // don't forget about sending + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + return; + } + BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + if (result == 0) { + BLog(BLOG_ERROR, "PR_Read returned 0"); + connection_report_error(o); + return; + } + + ASSERT(result > 0) + ASSERT(result <= o->recv_avail) + + // set no recv data + o->recv_avail = -1; + + // don't forget about sending + if (o->send_len > 0) { + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + + // finish receive operation + StreamRecvInterface_Done(&o->recv_if, result); + } break; + + default: + ASSERT(0); + } + + return; +} + +static void connection_recv_job_handler (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail > 0) + + connection_try_recv(o); + return; +} + +static void connection_try_handshake (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(!o->up) + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE); + } + return; + } + + // try handshake + SECStatus res = SSL_ForceHandshake(o->prfd); + if (res == SECFailure) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + // init up + connection_init_up(o); + + // report up + o->handler(o->user, BSSLCONNECTION_EVENT_UP); + return; +} + +static void connection_try_send (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->send_len > 0) + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE); + } + return; + } + + // send + PRInt32 res = PR_Write(o->prfd, o->send_data, o->send_len); + if (res < 0) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + ASSERT(res > 0) + ASSERT(res <= o->send_len) + + // set no send data + o->send_len = -1; + + // done + StreamPassInterface_Done(&o->send_if, res); +} + +static void connection_try_recv (BSSLConnection *o) +{ + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail > 0) + + // unset recv job + BPending_Unset(&o->recv_job); + + // continue in threadwork if requested + if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (o->backend->threadwork_state == THREADWORK_STATE_NONE) { + backend_threadwork_start(o->backend, THREADWORK_STATE_READ); + } + return; + } + + // recv + PRInt32 res = PR_Read(o->prfd, o->recv_data, o->recv_avail); + if (res < 0) { + PRErrorCode error = PR_GetError(); + if (error == PR_WOULD_BLOCK_ERROR) { + return; + } + BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error); + connection_report_error(o); + return; + } + + if (res == 0) { + BLog(BLOG_ERROR, "PR_Read returned 0"); + connection_report_error(o); + return; + } + + ASSERT(res > 0) + ASSERT(res <= o->recv_avail) + + // set no recv data + o->recv_avail = -1; + + // done + StreamRecvInterface_Done(&o->recv_if, res); +} + +static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->send_len == -1) + ASSERT(data_len > 0) + +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) + o->user_io_started = 1; +#endif + + // limit amount for PR_Write + if (data_len > INT32_MAX) { + data_len = INT32_MAX; + } + + // set send data + o->send_data = data; + o->send_len = data_len; + + // start sending + connection_try_send(o); +} + +static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_error) + ASSERT(o->up) + ASSERT(o->recv_avail == -1) + ASSERT(data_len > 0) + +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) + o->user_io_started = 1; +#endif + + // limit amount for PR_Read + if (data_len > INT32_MAX) { + data_len = INT32_MAX; + } + + // set recv data + o->recv_data = data; + o->recv_avail = data_len; + + // start receiving + connection_try_recv(o); +} + +int BSSLConnection_GlobalInit (void) +{ + ASSERT(!bprconnection_initialized) + + if ((bprconnection_identity = PR_GetUniqueIdentity("BSSLConnection")) == PR_INVALID_IO_LAYER) { + BLog(BLOG_ERROR, "PR_GetUniqueIdentity failed"); + return 0; + } + + bprconnection_initialized = 1; + + return 1; +} + +int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags) +{ + ASSERT(bprconnection_initialized) + ASSERT(!(flags & ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO))) + ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || twd) + ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_IO) || twd) + + // don't do stuff in threads if threads aren't available + if (((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) && + !BThreadWorkDispatcher_UsingThreads(twd) + ) { + BLog(BLOG_WARNING, "SSL operations in threads requested but threads are not available"); + flags &= ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO); + } + + // allocate backend + struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)malloc(sizeof(*b)); + if (!b) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init mutexes + if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { + if (!BMutex_Init(&b->send_buf_mutex)) { + BLog(BLOG_ERROR, "BMutex_Init failed"); + goto fail1; + } + + if (!BMutex_Init(&b->recv_buf_mutex)) { + BLog(BLOG_ERROR, "BMutex_Init failed"); + goto fail2; + } + } + + // init arguments + b->send_if = send_if; + b->recv_if = recv_if; + b->twd = twd; + b->flags = flags; + + // init interfaces + StreamPassInterface_Sender_Init(b->send_if, (StreamPassInterface_handler_done)backend_send_if_handler_done, b); + StreamRecvInterface_Receiver_Init(b->recv_if, (StreamRecvInterface_handler_done)backend_recv_if_handler_done, b); + + // set no connection + b->con = NULL; + + // init send buffer + b->send_busy = 0; + b->send_len = 0; + b->send_pos = 0; + + // init recv buffer + b->recv_busy = 0; + b->recv_pos = 0; + b->recv_len = 0; + + // set threadwork state + b->threadwork_state = THREADWORK_STATE_NONE; + + // init prfd + memset(prfd, 0, sizeof(*prfd)); + prfd->methods = &methods; + prfd->secret = (PRFilePrivate *)b; + prfd->identity = bprconnection_identity; + + return 1; + + if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) { +fail2: + BMutex_Free(&b->send_buf_mutex); + } +fail1: + free(b); +fail0: + return 0; +} + +void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user, + BSSLConnection_handler handler) +{ + ASSERT(force_handshake == 0 || force_handshake == 1) + ASSERT(handler) + ASSERT(bprconnection_initialized) + ASSERT(get_bottom(prfd)->identity == bprconnection_identity) + ASSERT(!((struct BSSLConnection_backend *)(get_bottom(prfd)->secret))->con) + + // init arguments + o->prfd = prfd; + o->pg = pg; + o->user = user; + o->handler = handler; + + // set backend + o->backend = (struct BSSLConnection_backend *)(get_bottom(prfd)->secret); + ASSERT(!o->backend->con) + ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) + + // set have no error + o->have_error = 0; + + // init init job + BPending_Init(&o->init_job, o->pg, (BPending_handler)connection_init_job_handler, o); + + if (force_handshake) { + // set not up + o->up = 0; + + // set init job + BPending_Set(&o->init_job); + } else { + // init up + connection_init_up(o); + } + + // set backend connection + o->backend->con = o; + +#ifndef NDEBUG + o->user_io_started = 0; + o->releasebuffers_called = 0; +#endif + + DebugError_Init(&o->d_err, o->pg); + DebugObject_Init(&o->d_obj); +} + +void BSSLConnection_Free (BSSLConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); +#ifndef NDEBUG + ASSERT(o->releasebuffers_called || !o->user_io_started) +#endif + ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE) + + if (o->up) { + // free recv job + BPending_Free(&o->recv_job); + + // free recv interface + StreamRecvInterface_Free(&o->recv_if); + + // free send interface + StreamPassInterface_Free(&o->send_if); + } + + // free init job + BPending_Free(&o->init_job); + + // unset backend connection + o->backend->con = NULL; +} + +void BSSLConnection_ReleaseBuffers (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->releasebuffers_called) +#endif + + // wait for threadwork to finish + if (o->backend->threadwork_state != THREADWORK_STATE_NONE) { + BThreadWork_Free(&o->backend->threadwork); + o->backend->threadwork_state = THREADWORK_STATE_NONE; + } + +#ifndef NDEBUG + o->releasebuffers_called = 1; +#endif +} + +StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return &o->send_if; +} + +StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->up) + + return &o->recv_if; +} diff --git a/external/badvpn_dns/nspr_support/BSSLConnection.h b/external/badvpn_dns/nspr_support/BSSLConnection.h new file mode 100644 index 00000000..1152cace --- /dev/null +++ b/external/badvpn_dns/nspr_support/BSSLConnection.h @@ -0,0 +1,116 @@ +/** + * @file BSSLConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BSSLCONNECTION_H +#define BADVPN_BSSLCONNECTION_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BSSLCONNECTION_EVENT_UP 1 +#define BSSLCONNECTION_EVENT_ERROR 2 + +#define BSSLCONNECTION_BUF_SIZE 4096 + +#define BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE (1 << 0) +#define BSSLCONNECTION_FLAG_THREADWORK_IO (1 << 1) + +typedef void (*BSSLConnection_handler) (void *user, int event); + +struct BSSLConnection_backend; + +typedef struct { + PRFileDesc *prfd; + BPendingGroup *pg; + void *user; + BSSLConnection_handler handler; + struct BSSLConnection_backend *backend; + int have_error; + int up; + BPending init_job; + StreamPassInterface send_if; + StreamRecvInterface recv_if; + BPending recv_job; + const uint8_t *send_data; + int send_len; + uint8_t *recv_data; + int recv_avail; +#ifndef NDEBUG + int user_io_started; + int releasebuffers_called; +#endif + DebugError d_err; + DebugObject d_obj; +} BSSLConnection; + +struct BSSLConnection_backend { + StreamPassInterface *send_if; + StreamRecvInterface *recv_if; + BThreadWorkDispatcher *twd; + int flags; + BSSLConnection *con; + uint8_t send_buf[BSSLCONNECTION_BUF_SIZE]; + int send_busy; + int send_pos; + int send_len; + uint8_t recv_buf[BSSLCONNECTION_BUF_SIZE]; + int recv_busy; + int recv_pos; + int recv_len; + int threadwork_state; + int threadwork_want_recv; + int threadwork_want_send; + BThreadWork threadwork; + SECStatus threadwork_result_sec; + PRInt32 threadwork_result_pr; + PRErrorCode threadwork_error; + BMutex send_buf_mutex; + BMutex recv_buf_mutex; +}; + +int BSSLConnection_GlobalInit (void) WARN_UNUSED; +int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags) WARN_UNUSED; + +void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user, + BSSLConnection_handler handler); +void BSSLConnection_Free (BSSLConnection *o); +void BSSLConnection_ReleaseBuffers (BSSLConnection *o); +StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o); +StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o); + +#endif diff --git a/external/badvpn_dns/nspr_support/CMakeLists.txt b/external/badvpn_dns/nspr_support/CMakeLists.txt new file mode 100644 index 00000000..d2eb3e75 --- /dev/null +++ b/external/badvpn_dns/nspr_support/CMakeLists.txt @@ -0,0 +1,5 @@ +set(NSPRSUPPORT_SOURCES + DummyPRFileDesc.c + BSSLConnection.c +) +badvpn_add_library(nspr_support "system;flow;threadwork" "${NSPR_LIBRARIES};${NSS_LIBRARIES}" "${NSPRSUPPORT_SOURCES}") diff --git a/external/badvpn_dns/nspr_support/DummyPRFileDesc.c b/external/badvpn_dns/nspr_support/DummyPRFileDesc.c new file mode 100644 index 00000000..543a3d51 --- /dev/null +++ b/external/badvpn_dns/nspr_support/DummyPRFileDesc.c @@ -0,0 +1,176 @@ +/** + * @file DummyPRFileDesc.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#ifndef NDEBUG +int dummyprfiledesc_initialized = 0; +#endif +PRDescIdentity dummyprfiledesc_identity; + +static PRStatus method_close (PRFileDesc *fd) +{ + return PR_SUCCESS; +} + +static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; +} + +static PRIntn _PR_InvalidIntn (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt16 _PR_InvalidInt16 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt32 _PR_InvalidInt32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt64 _PR_InvalidInt64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset32 _PR_InvalidOffset32 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PROffset64 _PR_InvalidOffset64 (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRStatus _PR_InvalidStatus (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc *_PR_InvalidDesc (void) +{ + ASSERT(0) + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +static PRIOMethods methods = { + (PRDescType)0, + method_close, + (PRReadFN)_PR_InvalidInt32, + (PRWriteFN)_PR_InvalidInt32, + (PRAvailableFN)_PR_InvalidInt32, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidOffset32, + (PRSeek64FN)_PR_InvalidOffset64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (PRWritevFN)_PR_InvalidInt32, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt32, + (PRSendFN)_PR_InvalidInt32, + (PRRecvfromFN)_PR_InvalidInt32, + (PRSendtoFN)_PR_InvalidInt32, + (PRPollFN)_PR_InvalidInt16, + (PRAcceptreadFN)_PR_InvalidInt32, + (PRTransmitfileFN)_PR_InvalidInt32, + (PRGetsocknameFN)_PR_InvalidStatus, + method_getpeername, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, + (PRSendfileFN)_PR_InvalidInt32, + (PRConnectcontinueFN)_PR_InvalidStatus, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn, + (PRReservedFN)_PR_InvalidIntn +}; + +int DummyPRFileDesc_GlobalInit (void) +{ + ASSERT(!dummyprfiledesc_initialized) + + if ((dummyprfiledesc_identity = PR_GetUniqueIdentity("DummyPRFileDesc")) == PR_INVALID_IO_LAYER) { + return 0; + } + + #ifndef NDEBUG + dummyprfiledesc_initialized = 1; + #endif + + return 1; +} + +void DummyPRFileDesc_Create (PRFileDesc *prfd) +{ + ASSERT(dummyprfiledesc_initialized) + + memset(prfd, 0, sizeof(*prfd)); + prfd->methods = &methods; + prfd->secret = NULL; + prfd->identity = dummyprfiledesc_identity; +} diff --git a/external/badvpn_dns/nspr_support/DummyPRFileDesc.h b/external/badvpn_dns/nspr_support/DummyPRFileDesc.h new file mode 100644 index 00000000..413f1058 --- /dev/null +++ b/external/badvpn_dns/nspr_support/DummyPRFileDesc.h @@ -0,0 +1,61 @@ +/** + * @file DummyPRFileDesc.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Dummy NSPR file descriptor (PRFileDesc). + * Used for creating a model SSL file descriptor to cache various stuff + * to improve performance. + */ + +#ifndef BADVPN_NSPRSUPPORT_DUMMYPRFILEDESC_H +#define BADVPN_NSPRSUPPORT_DUMMYPRFILEDESC_H + +#include + +#include + +extern PRDescIdentity dummyprfiledesc_identity; + +/** + * Globally initialize the dummy NSPR file descriptor backend. + * Must not have been called successfully. + * + * @return 1 on success, 0 on failure + */ +int DummyPRFileDesc_GlobalInit (void) WARN_UNUSED; + +/** + * Creates a dummy NSPR file descriptor. + * {@link DummyPRFileDesc_GlobalInit} must have been done. + * + * @param prfd uninitialized PRFileDesc structure + */ +void DummyPRFileDesc_Create (PRFileDesc *prfd); + +#endif diff --git a/external/badvpn_dns/predicate/BPredicate.c b/external/badvpn_dns/predicate/BPredicate.c new file mode 100644 index 00000000..5d5dadbb --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate.c @@ -0,0 +1,284 @@ +/** + * @file BPredicate.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int eval_predicate_node (BPredicate *p, struct predicate_node *root); + +void yyerror (YYLTYPE *yylloc, yyscan_t scanner, struct predicate_node **result, char *str) +{ +} + +static int string_comparator (void *user, char *s1, char *s2) +{ + int cmp = strcmp(s1, s2); + return B_COMPARE(cmp, 0); +} + +static int eval_function (BPredicate *p, struct predicate_node *root) +{ + ASSERT(root->type == NODE_FUNCTION) + + // lookup function by name + ASSERT(root->function.name) + BAVLNode *tree_node; + if (!(tree_node = BAVL_LookupExact(&p->functions_tree, root->function.name))) { + BLog(BLOG_WARNING, "unknown function"); + return 0; + } + BPredicateFunction *func = UPPER_OBJECT(tree_node, BPredicateFunction, tree_node); + + // evaluate arguments + struct arguments_node *arg = root->function.args; + void *args[PREDICATE_MAX_ARGS]; + for (int i = 0; i < func->num_args; i++) { + if (!arg) { + BLog(BLOG_WARNING, "not enough arguments"); + return 0; + } + switch (func->args[i]) { + case PREDICATE_TYPE_BOOL: + if (arg->arg.type != ARGUMENT_PREDICATE) { + BLog(BLOG_WARNING, "expecting predicate argument"); + return 0; + } + if (!eval_predicate_node(p, arg->arg.predicate)) { + return 0; + } + args[i] = &arg->arg.predicate->eval_value; + break; + case PREDICATE_TYPE_STRING: + if (arg->arg.type != ARGUMENT_STRING) { + BLog(BLOG_WARNING, "expecting string argument"); + return 0; + } + args[i] = arg->arg.string; + break; + default: + ASSERT(0); + } + arg = arg->next; + } + + if (arg) { + BLog(BLOG_WARNING, "too many arguments"); + return 0; + } + + // call callback + #ifndef NDEBUG + p->in_function = 1; + #endif + int res = func->callback(func->user, args); + #ifndef NDEBUG + p->in_function = 0; + #endif + if (res != 0 && res != 1) { + BLog(BLOG_WARNING, "callback returned non-boolean"); + return 0; + } + + root->eval_value = res; + return 1; +} + +int eval_predicate_node (BPredicate *p, struct predicate_node *root) +{ + ASSERT(root) + + switch (root->type) { + case NODE_CONSTANT: + root->eval_value = root->constant.val; + return 1; + case NODE_NEG: + if (!eval_predicate_node(p, root->neg.op)) { + return 0; + } + root->eval_value = !root->neg.op->eval_value; + return 1; + case NODE_CONJUNCT: + if (!eval_predicate_node(p, root->conjunct.op1)) { + return 0; + } + if (!root->conjunct.op1->eval_value) { + root->eval_value = 0; + return 1; + } + if (!eval_predicate_node(p, root->conjunct.op2)) { + return 0; + } + if (!root->conjunct.op2->eval_value) { + root->eval_value = 0; + return 1; + } + root->eval_value = 1; + return 1; + case NODE_DISJUNCT: + if (!eval_predicate_node(p, root->disjunct.op1)) { + return 0; + } + if (root->disjunct.op1->eval_value) { + root->eval_value = 1; + return 1; + } + if (!eval_predicate_node(p, root->disjunct.op2)) { + return 0; + } + if (root->disjunct.op2->eval_value) { + root->eval_value = 1; + return 1; + } + root->eval_value = 0; + return 1; + case NODE_FUNCTION: + return eval_function(p, root); + default: + ASSERT(0) + return 0; + } +} + +int BPredicate_Init (BPredicate *p, char *str) +{ + // initialize input buffer object + LexMemoryBufferInput input; + LexMemoryBufferInput_Init(&input, str, strlen(str)); + + // initialize lexical analyzer + yyscan_t scanner; + yylex_init_extra(&input, &scanner); + + // parse + struct predicate_node *root = NULL; + int result = yyparse(scanner, &root); + + // free lexical analyzer + yylex_destroy(scanner); + + // check for errors + if (LexMemoryBufferInput_HasError(&input) || result != 0 || !root) { + if (root) { + free_predicate_node(root); + } + return 0; + } + + // init tree + p->root = root; + + // init functions tree + BAVL_Init(&p->functions_tree, OFFSET_DIFF(BPredicateFunction, name, tree_node), (BAVL_comparator)string_comparator, NULL); + + // init debuggind + #ifndef NDEBUG + p->in_function = 0; + #endif + + // init debug object + DebugObject_Init(&p->d_obj); + + return 1; +} + +void BPredicate_Free (BPredicate *p) +{ + ASSERT(BAVL_IsEmpty(&p->functions_tree)) + ASSERT(!p->in_function) + + // free debug object + DebugObject_Free(&p->d_obj); + + // free tree + free_predicate_node((struct predicate_node *)p->root); +} + +int BPredicate_Eval (BPredicate *p) +{ + ASSERT(!p->in_function) + + if (!eval_predicate_node(p, (struct predicate_node *)p->root)) { + return -1; + } + + return ((struct predicate_node *)p->root)->eval_value; +} + +void BPredicateFunction_Init (BPredicateFunction *o, BPredicate *p, char *name, int *args, int num_args, BPredicate_callback callback, void *user) +{ + ASSERT(strlen(name) <= PREDICATE_MAX_NAME) + ASSERT(!BAVL_LookupExact(&p->functions_tree, name)) + ASSERT(num_args >= 0) + ASSERT(num_args <= PREDICATE_MAX_ARGS) + for (int i = 0; i < num_args; i++) { + ASSERT(args[i] == PREDICATE_TYPE_BOOL || args[i] == PREDICATE_TYPE_STRING) + } + ASSERT(!p->in_function) + + // init arguments + o->p = p; + strcpy(o->name, name); + memcpy(o->args, args, num_args * sizeof(int)); + o->num_args = num_args; + o->callback = callback; + o->user = user; + + // add to tree + ASSERT_EXECUTE(BAVL_Insert(&p->functions_tree, &o->tree_node, NULL)) + + // init debug object + DebugObject_Init(&o->d_obj); +} + +void BPredicateFunction_Free (BPredicateFunction *o) +{ + ASSERT(!o->p->in_function) + + BPredicate *p = o->p; + + // free debug object + DebugObject_Free(&o->d_obj); + + // remove from tree + BAVL_Remove(&p->functions_tree, &o->tree_node); +} diff --git a/external/badvpn_dns/predicate/BPredicate.h b/external/badvpn_dns/predicate/BPredicate.h new file mode 100644 index 00000000..04b3aa51 --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate.h @@ -0,0 +1,177 @@ +/** + * @file BPredicate.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that parses and evaluates a logical expression. + * Allows the user to define custom functions than can be + * used in the expression. + * + * Syntax and semantics for logical expressions: + * + * - true + * Logical true constant. Evaluates to 1. + * + * - false + * Logical false constant. Evaluates to 0. + * + * - NOT expression + * Logical negation. If the expression evaluates to error, the + * negation evaluates to error. + * + * - expression OR expression + * Logical disjunction. The second expression is only evaluated + * if the first expression evaluates to false. If a sub-expression + * evaluates to error, the disjunction evaluates to error. + * + * - expression AND expression + * Logical conjunction. The second expression is only evaluated + * if the first expression evaluates to true. If a sub-expression + * evaluates to error, the conjunction evaluates to error. + * + * - function(arg, ..., arg) + * Evaluation of a user-provided function (function is the name of the + * function, [a-zA-Z0-9_]+). + * If the function with the given name does not exist, it evaluates to + * error. + * Arguments are evaluated from left to right. Each argument can either + * be a logical expression or a string (characters enclosed in double + * quotes, without any double quote). + * If an argument is encountered, but all needed arguments have already + * been evaluated, the function evaluates to error. + * If an argument is of wrong type, it is not evaluated and the function + * evaluates to error. + * If an argument evaluates to error, the function evaluates to error. + * If after all arguments have been evaluated, the function needs more + * arguments, it evaluates to error. + * Then the handler function is called. If it returns anything other + * than 1 and 0, the function evaluates to error. Otherwise it evaluates + * to what the handler function returned. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_H +#define BADVPN_PREDICATE_BPREDICATE_H + +#include +#include +#include + +#define PREDICATE_TYPE_BOOL 1 +#define PREDICATE_TYPE_STRING 2 + +#define PREDICATE_MAX_NAME 16 +#define PREDICATE_MAX_ARGS 16 + +/** + * Handler function called when evaluating a custom function in the predicate. + * + * @param user value passed to {@link BPredicateFunction_Init} + * @param args arguments to the function. Points to an array of pointers (as many as the + * function has arguments), where each pointer points to either to an int or + * a zero-terminated string (depending on the type of the argument). + * @return 1 for true, 0 for false, -1 for error + */ +typedef int (*BPredicate_callback) (void *user, void **args); + +/** + * Object that parses and evaluates a logical expression. + * Allows the user to define custom functions than can be + * used in the expression. + */ +typedef struct { + DebugObject d_obj; + void *root; + BAVL functions_tree; + #ifndef NDEBUG + int in_function; + #endif +} BPredicate; + +/** + * Object that represents a custom function in {@link BPredicate}. + */ +typedef struct { + DebugObject d_obj; + BPredicate *p; + char name[PREDICATE_MAX_NAME + 1]; + int args[PREDICATE_MAX_ARGS]; + int num_args; + BPredicate_callback callback; + void *user; + BAVLNode tree_node; +} BPredicateFunction; + +/** + * Initializes the object. + * + * @param p the object + * @param str logical expression + * @return 1 on success, 0 on failure + */ +int BPredicate_Init (BPredicate *p, char *str) WARN_UNUSED; + +/** + * Frees the object. + * Must have no custom functions. + * Must not be called from function handlers. + * + * @param p the object + */ +void BPredicate_Free (BPredicate *p); + +/** + * Evaluates the logical expression. + * Must not be called from function handlers. + * + * @param p the object + * @return 1 for true, 0 for false, -1 for error + */ +int BPredicate_Eval (BPredicate *p); + +/** + * Registers a custom function for {@link BPredicate}. + * Must not be called from function handlers. + * + * @param o the object + * @param p predicate to register the function for + * @param args array of argument types. Each type is either PREDICATE_TYPE_BOOL or PREDICATE_TYPE_STRING. + * @param num_args number of arguments for the function. Must be >=0 and <=PREDICATE_MAX_ARGS. + * @param callback handler to call to evaluate the function + * @param user value to pass to handler + */ +void BPredicateFunction_Init (BPredicateFunction *o, BPredicate *p, char *name, int *args, int num_args, BPredicate_callback callback, void *user); + +/** + * Removes a custom function for {@link BPredicate}. + * Must not be called from function handlers. + * + * @param o the object + */ +void BPredicateFunction_Free (BPredicateFunction *o); + +#endif diff --git a/external/badvpn_dns/predicate/BPredicate.l b/external/badvpn_dns/predicate/BPredicate.l new file mode 100644 index 00000000..71bfd2f5 --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate.l @@ -0,0 +1,83 @@ +/** + * @file BPredicate.l + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} lexer file. + */ + +%{ + +#include +#include + +#include +#include + +#include + +#define YY_INPUT(buffer, res, max_size) \ + int bytes_read = LexMemoryBufferInput_Read((LexMemoryBufferInput *)yyget_extra(yyscanner), buffer, max_size); \ + res = (bytes_read == 0 ? YY_NULL : bytes_read); + +%} + +%option reentrant stack noyywrap bison-bridge bison-locations never-interactive nounistd + +%% +\( return SPAR; +\) return EPAR; +, return COMMA; +AND return AND; +OR return OR; +NOT return NOT; +true return CONSTANT_TRUE; +false return CONSTANT_FALSE; +[a-zA-Z0-9_]+ { + int l = strlen(yytext); + char *p = (char *)malloc(l + 1); + if (p) { + memcpy(p, yytext, l); + p[l] = '\0'; + } + yylval->text = p; + return NAME; + } +\"[^\"]*\" { + int l = strlen(yytext); + char *p = (char *)malloc(l - 1); + if (p) { + memcpy(p, yytext + 1, l - 2); + p[l - 2] = '\0'; + } + yylval->text = p; + return STRING; + } +[ \t\n]+ ; +. LexMemoryBufferInput_SetError((LexMemoryBufferInput *)yyget_extra(yyscanner)); return 0; // remember failure and report EOF +%% diff --git a/external/badvpn_dns/predicate/BPredicate.y b/external/badvpn_dns/predicate/BPredicate.y new file mode 100644 index 00000000..f48df45e --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate.y @@ -0,0 +1,345 @@ +/** + * @file BPredicate.y + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} grammar file. + */ + +%{ + +#include + +#include +#include + +#define YYLEX_PARAM scanner + +static struct predicate_node * make_constant (int val) +{ + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + return NULL; + } + + n->type = NODE_CONSTANT; + n->constant.val = val; + + return n; +} + +static struct predicate_node * make_negation (struct predicate_node *op) +{ + if (!op) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_NEG; + n->neg.op = op; + + return n; + +fail: + if (op) { + free_predicate_node(op); + } + return NULL; +} + +static struct predicate_node * make_conjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_CONJUNCT; + n->conjunct.op1 = op1; + n->conjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_disjunction (struct predicate_node *op1, struct predicate_node *op2) +{ + if (!op1 || !op2) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_DISJUNCT; + n->disjunct.op1 = op1; + n->disjunct.op2 = op2; + + return n; + +fail: + if (op1) { + free_predicate_node(op1); + } + if (op2) { + free_predicate_node(op2); + } + return NULL; +} + +static struct predicate_node * make_function (char *name, struct arguments_node *args, int need_args) +{ + if (!name || (!args && need_args)) { + goto fail; + } + + struct predicate_node *n = (struct predicate_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->type = NODE_FUNCTION; + n->function.name = name; + n->function.args = args; + + return n; + +fail: + if (name) { + free(name); + } + if (args) { + free_arguments_node(args); + } + return NULL; +} + +static struct arguments_node * make_arguments (struct arguments_arg arg, struct arguments_node *next, int need_next) +{ + if (arg.type == ARGUMENT_INVALID || (!next && need_next)) { + goto fail; + } + + struct arguments_node *n = (struct arguments_node *)malloc(sizeof(*n)); + if (!n) { + goto fail; + } + + n->arg = arg; + n->next = next; + + return n; + +fail: + free_argument(arg); + if (next) { + free_arguments_node(next); + } + return NULL; +} + +static struct arguments_arg make_argument_predicate (struct predicate_node *pr) +{ + struct arguments_arg ret; + + if (!pr) { + goto fail; + } + + ret.type = ARGUMENT_PREDICATE; + ret.predicate = pr; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +static struct arguments_arg make_argument_string (char *string) +{ + struct arguments_arg ret; + + if (!string) { + goto fail; + } + + ret.type = ARGUMENT_STRING; + ret.string = string; + + return ret; + +fail: + ret.type = ARGUMENT_INVALID; + return ret; +} + +%} + +%pure-parser +%locations +%parse-param {void *scanner} +%parse-param {struct predicate_node **result} + +%union { + char *text; + struct predicate_node *node; + struct arguments_node *arg_node; + struct predicate_node nfaw; + struct arguments_arg arg_arg; +}; + +// token types +%token STRING NAME +%token PEER1_NAME PEER2_NAME AND OR NOT SPAR EPAR CONSTANT_TRUE CONSTANT_FALSE COMMA + +// string token destructor +%destructor { + free($$); +} STRING NAME + +// return values +%type predicate constant parentheses neg conjunct disjunct function +%type arguments +%type argument + +// predicate node destructor +%destructor { + if ($$) { + free_predicate_node($$); + } +} predicate constant parentheses neg conjunct disjunct function + +// argument node destructor +%destructor { + if ($$) { + free_arguments_node($$); + } +} arguments + +// argument argument destructor +%destructor { + free_argument($$); +} argument + +%left OR +%left AND +%nonassoc NOT +%right COMMA + +%% + +input: + predicate { + *result = $1; + } + ; + +predicate: constant | parentheses | neg | conjunct | disjunct | function; + +constant: + CONSTANT_TRUE { + $$ = make_constant(1); + } + | + CONSTANT_FALSE { + $$ = make_constant(0); + } + ; + +parentheses: + SPAR predicate EPAR { + $$ = $2; + } + ; + +neg: + NOT predicate { + $$ = make_negation($2); + } + ; + +conjunct: + predicate AND predicate { + $$ = make_conjunction($1, $3); + } + ; + +disjunct: + predicate OR predicate { + $$ = make_disjunction($1, $3); + } + ; + +function: + NAME SPAR EPAR { + $$ = make_function($1, NULL, 0); + } + | + NAME SPAR arguments EPAR { + $$ = make_function($1, $3, 1); + } + ; + +arguments: + argument { + $$ = make_arguments($1, NULL, 0); + } + | + argument COMMA arguments { + $$ = make_arguments($1, $3, 1); + } + ; + +argument: + predicate { + $$ = make_argument_predicate($1); + } + | + STRING { + $$ = make_argument_string($1); + } + ; diff --git a/external/badvpn_dns/predicate/BPredicate_internal.h b/external/badvpn_dns/predicate/BPredicate_internal.h new file mode 100644 index 00000000..12db5549 --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate_internal.h @@ -0,0 +1,154 @@ +/** + * @file BPredicate_internal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} expression tree definitions and functions. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_INTERNAL_H +#define BADVPN_PREDICATE_BPREDICATE_INTERNAL_H + +#include + +#define NODE_CONSTANT 0 +#define NODE_NEG 2 +#define NODE_CONJUNCT 3 +#define NODE_DISJUNCT 4 +#define NODE_FUNCTION 5 + +struct arguments_node; + +struct predicate_node { + int type; + union { + struct { + int val; + } constant; + struct { + struct predicate_node *op; + } neg; + struct { + struct predicate_node *op1; + struct predicate_node *op2; + } conjunct; + struct { + struct predicate_node *op1; + struct predicate_node *op2; + } disjunct; + struct { + char *name; + struct arguments_node *args; + } function; + }; + int eval_value; +}; + +#define ARGUMENT_INVALID 0 +#define ARGUMENT_PREDICATE 1 +#define ARGUMENT_STRING 2 + +struct arguments_arg { + int type; + union { + struct predicate_node *predicate; + char *string; + }; +}; + +struct arguments_node { + struct arguments_arg arg; + struct arguments_node *next; +}; + +static void free_predicate_node (struct predicate_node *root); +static void free_argument (struct arguments_arg arg); +static void free_arguments_node (struct arguments_node *n); + +void free_predicate_node (struct predicate_node *root) +{ + ASSERT(root) + + switch (root->type) { + case NODE_CONSTANT: + break; + case NODE_NEG: + free_predicate_node(root->neg.op); + break; + case NODE_CONJUNCT: + free_predicate_node(root->conjunct.op1); + free_predicate_node(root->conjunct.op2); + break; + case NODE_DISJUNCT: + free_predicate_node(root->disjunct.op1); + free_predicate_node(root->disjunct.op2); + break; + case NODE_FUNCTION: + free(root->function.name); + if (root->function.args) { + free_arguments_node(root->function.args); + } + break; + default: + ASSERT(0) + break; + } + + free(root); +} + +void free_argument (struct arguments_arg arg) +{ + switch (arg.type) { + case ARGUMENT_INVALID: + break; + case ARGUMENT_PREDICATE: + free_predicate_node(arg.predicate); + break; + case ARGUMENT_STRING: + free(arg.string); + break; + default: + ASSERT(0); + } +} + +void free_arguments_node (struct arguments_node *n) +{ + ASSERT(n) + + free_argument(n->arg); + + if (n->next) { + free_arguments_node(n->next); + } + + free(n); +} + +#endif diff --git a/external/badvpn_dns/predicate/BPredicate_parser.h b/external/badvpn_dns/predicate/BPredicate_parser.h new file mode 100644 index 00000000..e7f4a7bd --- /dev/null +++ b/external/badvpn_dns/predicate/BPredicate_parser.h @@ -0,0 +1,47 @@ +/** + * @file BPredicate_parser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * {@link BPredicate} parser definitions. + */ + +#ifndef BADVPN_PREDICATE_BPREDICATE_PARSER_H +#define BADVPN_PREDICATE_BPREDICATE_PARSER_H + +#define YY_NO_UNISTD_H + +#include + +#include +#include + +// implemented in BPredicate.c +void yyerror (YYLTYPE *yylloc, yyscan_t scanner, struct predicate_node **result, char *str); + +#endif diff --git a/external/badvpn_dns/predicate/CMakeLists.txt b/external/badvpn_dns/predicate/CMakeLists.txt new file mode 100644 index 00000000..dfd852e0 --- /dev/null +++ b/external/badvpn_dns/predicate/CMakeLists.txt @@ -0,0 +1,6 @@ +set(PREDICATE_SOURCES + BPredicate.c + ${PROJECT_SOURCE_DIR}/generated/flex_BPredicate.c + ${PROJECT_SOURCE_DIR}/generated/bison_BPredicate.c +) +badvpn_add_library(predicate "system" "" "${PREDICATE_SOURCES}") diff --git a/external/badvpn_dns/predicate/LexMemoryBufferInput.h b/external/badvpn_dns/predicate/LexMemoryBufferInput.h new file mode 100644 index 00000000..e8f17309 --- /dev/null +++ b/external/badvpn_dns/predicate/LexMemoryBufferInput.h @@ -0,0 +1,86 @@ +/** + * @file LexMemoryBufferInput.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that can be used by a lexer to read input from a memory buffer. + */ + +#ifndef BADVPN_PREDICATE_LEXMEMORYBUFFERINPUT_H +#define BADVPN_PREDICATE_LEXMEMORYBUFFERINPUT_H + +#include + +#include + +typedef struct { + char *buf; + int len; + int pos; + int error; +} LexMemoryBufferInput; + +static void LexMemoryBufferInput_Init (LexMemoryBufferInput *input, char *buf, int len) +{ + input->buf = buf; + input->len = len; + input->pos = 0; + input->error = 0; +} + +static int LexMemoryBufferInput_Read (LexMemoryBufferInput *input, char *dest, int len) +{ + ASSERT(dest) + ASSERT(len > 0) + + if (input->pos >= input->len) { + return 0; + } + + int to_read = input->len - input->pos; + if (to_read > len) { + to_read = len; + } + + memcpy(dest, input->buf + input->pos, to_read); + input->pos += to_read; + + return to_read; +} + +static void LexMemoryBufferInput_SetError (LexMemoryBufferInput *input) +{ + input->error = 1; +} + +static int LexMemoryBufferInput_HasError (LexMemoryBufferInput *input) +{ + return input->error; +} + +#endif diff --git a/external/badvpn_dns/protocol/addr.bproto b/external/badvpn_dns/protocol/addr.bproto new file mode 100644 index 00000000..f020350d --- /dev/null +++ b/external/badvpn_dns/protocol/addr.bproto @@ -0,0 +1,11 @@ +// message for an AddrProto address +message addr { + // address type. from addr.h + required uint8 type = 1; + // for IPv4 and IPv6 addresses, the port (network byte order) + optional data("2") ip_port = 2; + // for IPv4 addresses, the IP address + optional data("4") ipv4_addr = 3; + // for IPv6 addresses, the IP address + optional data("16") ipv6_addr = 4; +}; diff --git a/external/badvpn_dns/protocol/addr.h b/external/badvpn_dns/protocol/addr.h new file mode 100644 index 00000000..9d202654 --- /dev/null +++ b/external/badvpn_dns/protocol/addr.h @@ -0,0 +1,207 @@ +/** + * @file addr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * AddrProto, a protocol for encoding network addresses. + * + * AddrProto is built with BProto, the protocol and code generator for building + * custom message protocols. The BProto specification file is addr.bproto. + */ + +#ifndef BADVPN_PROTOCOL_ADDR_H +#define BADVPN_PROTOCOL_ADDR_H + +#include +#include + +#include +#include +#include + +#include + +#include + +#define ADDR_TYPE_IPV4 1 +#define ADDR_TYPE_IPV6 2 + +#define ADDR_SIZE_IPV4 (addr_SIZEtype + addr_SIZEip_port + addr_SIZEipv4_addr) +#define ADDR_SIZE_IPV6 (addr_SIZEtype + addr_SIZEip_port + addr_SIZEipv6_addr) + +/** + * Determines if the given address is supported by AddrProto. + * Depends only on the type of the address. + * + * @param addr address to check. Must be recognized according to {@link BAddr_IsRecognized}. + * @return 1 if supported, 0 if not + */ +static int addr_supported (BAddr addr); + +/** + * Determines the size of the given address when encoded by AddrProto. + * Depends only on the type of the address. + * + * @param addr address to check. Must be supported according to {@link addr_supported}. + * @return encoded size + */ +static int addr_size (BAddr addr); + +/** + * Encodes an address according to AddrProto. + * + * @param out output buffer. Must have at least addr_size(addr) space. + * @param addr address to encode. Must be supported according to {@link addr_supported}. + */ +static void addr_write (uint8_t *out, BAddr addr); + +/** + * Decodes an address according to AddrProto. + * + * @param data input buffer containing the address to decode + * @param data_len size of input. Must be >=0. + * @param out_addr the decoded address will be stored here on success + * @return 1 on success, 0 on failure + */ +static int addr_read (uint8_t *data, int data_len, BAddr *out_addr) WARN_UNUSED; + +int addr_supported (BAddr addr) +{ + BAddr_Assert(&addr); + + switch (addr.type) { + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + return 1; + default: + return 0; + } +} + +int addr_size (BAddr addr) +{ + ASSERT(addr_supported(addr)) + + switch (addr.type) { + case BADDR_TYPE_IPV4: + return ADDR_SIZE_IPV4; + case BADDR_TYPE_IPV6: + return ADDR_SIZE_IPV6; + default: + ASSERT(0) + return 0; + } +} + +void addr_write (uint8_t *out, BAddr addr) +{ + ASSERT(addr_supported(addr)) + + addrWriter writer; + addrWriter_Init(&writer, out); + + switch (addr.type) { + case BADDR_TYPE_IPV4: { + addrWriter_Addtype(&writer, ADDR_TYPE_IPV4); + uint8_t *out_port = addrWriter_Addip_port(&writer); + memcpy(out_port, &addr.ipv4.port, sizeof(addr.ipv4.port)); + uint8_t *out_addr = addrWriter_Addipv4_addr(&writer); + memcpy(out_addr, &addr.ipv4.ip, sizeof(addr.ipv4.ip)); + } break; + case BADDR_TYPE_IPV6: { + addrWriter_Addtype(&writer, ADDR_TYPE_IPV6); + uint8_t *out_port = addrWriter_Addip_port(&writer); + memcpy(out_port, &addr.ipv6.port, sizeof(addr.ipv6.port)); + uint8_t *out_addr = addrWriter_Addipv6_addr(&writer); + memcpy(out_addr, addr.ipv6.ip, sizeof(addr.ipv6.ip)); + } break; + default: + ASSERT(0); + } + + int len = addrWriter_Finish(&writer); + B_USE(len) + + ASSERT(len == addr_size(addr)) +} + +int addr_read (uint8_t *data, int data_len, BAddr *out_addr) +{ + ASSERT(data_len >= 0) + + addrParser parser; + if (!addrParser_Init(&parser, data, data_len)) { + BLog(BLOG_ERROR, "failed to parse addr"); + return 0; + } + + uint8_t type = 0; // to remove warning + addrParser_Gettype(&parser, &type); + + switch (type) { + case ADDR_TYPE_IPV4: { + uint8_t *port_data; + if (!addrParser_Getip_port(&parser, &port_data)) { + BLog(BLOG_ERROR, "port missing for IPv4 address"); + return 0; + } + uint8_t *addr_data; + if (!addrParser_Getipv4_addr(&parser, &addr_data)) { + BLog(BLOG_ERROR, "address missing for IPv4 address"); + return 0; + } + uint16_t port; + uint32_t addr; + memcpy(&port, port_data, sizeof(port)); + memcpy(&addr, addr_data, sizeof(addr)); + BAddr_InitIPv4(out_addr, addr, port); + } break; + case ADDR_TYPE_IPV6: { + uint8_t *port_data; + if (!addrParser_Getip_port(&parser, &port_data)) { + BLog(BLOG_ERROR, "port missing for IPv6 address"); + return 0; + } + uint8_t *addr_data; + if (!addrParser_Getipv6_addr(&parser, &addr_data)) { + BLog(BLOG_ERROR, "address missing for IPv6 address"); + return 0; + } + uint16_t port; + memcpy(&port, port_data, sizeof(port)); + BAddr_InitIPv6(out_addr, addr_data, port); + } break; + default: + BLog(BLOG_ERROR, "unknown address type %d", (int)type); + return 0; + } + + return 1; +} + +#endif diff --git a/external/badvpn_dns/protocol/dataproto.h b/external/badvpn_dns/protocol/dataproto.h new file mode 100644 index 00000000..998d9535 --- /dev/null +++ b/external/badvpn_dns/protocol/dataproto.h @@ -0,0 +1,91 @@ +/** + * @file dataproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for DataProto, the protocol for data transport between VPN peers. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A DataProto packet consists of: + * - the header (struct {@link dataproto_header}) + * - between zero and DATAPROTO_MAX_PEER_IDS destination peer IDs (struct {@link dataproto_peer_id}) + * - the payload, e.g. Ethernet frame + */ + +#ifndef BADVPN_PROTOCOL_DATAPROTO_H +#define BADVPN_PROTOCOL_DATAPROTO_H + +#include + +#include +#include + +#define DATAPROTO_MAX_PEER_IDS 1 + +#define DATAPROTO_FLAGS_RECEIVING_KEEPALIVES 1 + +/** + * DataProto header. + */ +B_START_PACKED +struct dataproto_header { + /** + * Bitwise OR of flags. Possible flags: + * - DATAPROTO_FLAGS_RECEIVING_KEEPALIVES + * Indicates that when the peer sent this packet, it has received at least + * one packet from the other peer in the last keep-alive tolerance time. + */ + uint8_t flags; + + /** + * ID of the peer this frame originates from. + */ + peerid_t from_id; + + /** + * Number of destination peer IDs that follow. + * Must be <=DATAPROTO_MAX_PEER_IDS. + */ + peerid_t num_peer_ids; +} B_PACKED; +B_END_PACKED + +/** + * Structure for a destination peer ID in DataProto. + * Wraps a single peerid_t in a packed struct for easy access. + */ +B_START_PACKED +struct dataproto_peer_id { + peerid_t id; +} B_PACKED; +B_END_PACKED + +#define DATAPROTO_MAX_OVERHEAD (sizeof(struct dataproto_header) + DATAPROTO_MAX_PEER_IDS * sizeof(struct dataproto_peer_id)) + +#endif diff --git a/external/badvpn_dns/protocol/fragmentproto.h b/external/badvpn_dns/protocol/fragmentproto.h new file mode 100644 index 00000000..4d2315e7 --- /dev/null +++ b/external/badvpn_dns/protocol/fragmentproto.h @@ -0,0 +1,100 @@ +/** + * @file fragmentproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for FragmentProto, a protocol that allows sending of arbitrarily sized packets over + * a link with a fixed MTU. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A FragmentProto packet consists of a number of chunks. + * Each chunk consists of: + * - the chunk header (struct {@link fragmentproto_chunk_header}) + * - the chunk payload, i.e. part of the frame specified in the header + */ + +#ifndef BADVPN_PROTOCOL_FRAGMENTPROTO_H +#define BADVPN_PROTOCOL_FRAGMENTPROTO_H + +#include + +#include +#include + +typedef uint16_t fragmentproto_frameid; + +/** + * FragmentProto chunk header. + */ +B_START_PACKED +struct fragmentproto_chunk_header { + /** + * Identifier of the frame this chunk belongs to. + * Frames should be given ascending identifiers as they are encoded + * into chunks (except when the ID wraps to zero). + */ + fragmentproto_frameid frame_id; + + /** + * Position in the frame where this chunk starts. + */ + uint16_t chunk_start; + + /** + * Length of the chunk's payload. + */ + uint16_t chunk_len; + + /** + * Whether this is the last chunk of the frame, i.e. + * the total length of the frame is chunk_start + chunk_len. + */ + uint8_t is_last; +} B_PACKED; +B_END_PACKED + +/** + * Calculates how many chunks are needed at most for encoding one frame of the + * given maximum size with FragmentProto onto a carrier with a given MTU. + * This includes the case when the first chunk of a frame is not the first chunk + * in a FragmentProto packet. + * + * @param carrier_mtu MTU of the carrier, i.e. maximum length of FragmentProto packets. Must be >sizeof(struct fragmentproto_chunk_header). + * @param frame_mtu maximum frame size. Must be >=0. + * @return maximum number of chunks needed. Will be >0. + */ +static int fragmentproto_max_chunks_for_frame (int carrier_mtu, int frame_mtu) +{ + ASSERT(carrier_mtu > sizeof(struct fragmentproto_chunk_header)) + ASSERT(frame_mtu >= 0) + + return (bdivide_up(frame_mtu, (carrier_mtu - sizeof(struct fragmentproto_chunk_header))) + 1); +} + +#endif diff --git a/external/badvpn_dns/protocol/msgproto.bproto b/external/badvpn_dns/protocol/msgproto.bproto new file mode 100644 index 00000000..202931e2 --- /dev/null +++ b/external/badvpn_dns/protocol/msgproto.bproto @@ -0,0 +1,43 @@ +// message for all MsgProto messages +message msg { + // message type, from msgproto.h + required uint16 type = 1; + // message payload. Is itself one of the messages below + // for "youconnect", "seed" and "confirmseed" messages, + // and empty for other messages + required data payload = 2; +}; + +// "youconnect" message payload +message msg_youconnect { + // external addresses to try; one or more msg_youconnect_addr messages + required repeated data addr = 1; + // encryption key if using UDP and encryption is enabled + optional data key = 2; + // password if using TCP + optional uint64 password = 3; +}; + +// an external address +message msg_youconnect_addr { + // scope name for this address + required data name = 1; + // address according to AddrProto + required data addr = 2; +}; + +// "seed" message payload +message msg_seed { + // identifier for the seed being send + required uint16 seed_id = 1; + // seed encryption key + required data key = 2; + // seed IV + required data iv = 3; +}; + +// "confirmseed" message payload +message msg_confirmseed { + // identifier for the seed being confirmed + required uint16 seed_id = 1; +}; diff --git a/external/badvpn_dns/protocol/msgproto.h b/external/badvpn_dns/protocol/msgproto.h new file mode 100644 index 00000000..286abb08 --- /dev/null +++ b/external/badvpn_dns/protocol/msgproto.h @@ -0,0 +1,76 @@ +/** + * @file msgproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * MsgProto is used by each pair of VPN peers as messages through the server, in order to + * establish a direct data connection. MsgProto operates on top of the SCProto message + * service, optionally secured with SSL; see {@link scproto.h} for details. + * + * MsgProto is built with BProto, the protocol and code generator for building + * custom message protocols. The BProto specification file is msgproto.bproto. + * + * It goes roughly like that: + * + * We name one peer the master and the other the slave. The master is the one with + * greater ID. + * When the peers get to know about each other, the master starts the binding procedure. + * It binds/listens to an address, and sends the slave the "youconnect" message. It + * contains a list of external addresses for that bind address and additional parameters. + * Each external address includes a string called a scope name. The slave, which receives + * the "youconnect" message, finds the first external address whose scope it recognizes, + * and attempts to establish connection to that address. If it finds an address, buf fails + * at connecting, it sends "youretry", which makes the master restart the binding procedure + * after some time. If it however does not recognize any external address, it sends + * "cannotconnect" back to the master. + * When the master receives the "cannotconnect", it tries the next bind address, as described + * above. When the master runs out of bind addresses, it sends "cannotbind" to the slave. + * When the slave receives the "cannotbind", it starts its own binding procedure, similarly + * to what is described above, with master and slave reversed. First difference is if the + * master fails to connect to a recognized address, it doesn't send "youretry", but rather + * simply restarts the whole procedure after some time. The other difference is when the + * slave runs out of bind addresses, it not only sends "cannotbind" to the master, but + * registers relaying to the master. And in this case, when the master receives the "cannotbind", + * it doesn't start the binding procedure all all over, but registers relaying to the slave. + */ + +#ifndef BADVPN_PROTOCOL_MSGPROTO_H +#define BADVPN_PROTOCOL_MSGPROTO_H + +#include + +#define MSGID_YOUCONNECT 1 +#define MSGID_CANNOTCONNECT 2 +#define MSGID_CANNOTBIND 3 +#define MSGID_YOURETRY 5 +#define MSGID_SEED 6 +#define MSGID_CONFIRMSEED 7 + +#define MSG_MAX_PAYLOAD (SC_MAX_MSGLEN - msg_SIZEtype - msg_SIZEpayload(0)) + +#endif diff --git a/external/badvpn_dns/protocol/packetproto.h b/external/badvpn_dns/protocol/packetproto.h new file mode 100644 index 00000000..0f0982bd --- /dev/null +++ b/external/badvpn_dns/protocol/packetproto.h @@ -0,0 +1,68 @@ +/** + * @file packetproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for PacketProto, a protocol that allows sending of packets + * over a reliable stream connection. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * Packets are encoded into a stream by representing each packet with: + * - a 16-bit little-endian unsigned integer representing the length + * of the payload + * - that many bytes of payload + */ + +#ifndef BADVPN_PROTOCOL_PACKETPROTO_H +#define BADVPN_PROTOCOL_PACKETPROTO_H + +#include +#include + +#include + +/** + * PacketProto packet header. + * Wraps a single uint16_t in a packed struct for easy access. + */ +B_START_PACKED +struct packetproto_header +{ + /** + * Length of the packet payload that follows. + */ + uint16_t len; +} B_PACKED; +B_END_PACKED + +#define PACKETPROTO_ENCLEN(_len) (sizeof(struct packetproto_header) + (_len)) + +#define PACKETPROTO_MAXPAYLOAD UINT16_MAX + +#endif diff --git a/external/badvpn_dns/protocol/requestproto.h b/external/badvpn_dns/protocol/requestproto.h new file mode 100644 index 00000000..2ec3d0dc --- /dev/null +++ b/external/badvpn_dns/protocol/requestproto.h @@ -0,0 +1,50 @@ +/** + * @file requestproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_REQUESTPROTO_H +#define BADVPN_REQUESTPROTO_H + +#include + +#include + +#define REQUESTPROTO_TYPE_CLIENT_REQUEST 1 +#define REQUESTPROTO_TYPE_CLIENT_ABORT 2 +#define REQUESTPROTO_TYPE_SERVER_REPLY 3 +#define REQUESTPROTO_TYPE_SERVER_FINISHED 4 +#define REQUESTPROTO_TYPE_SERVER_ERROR 5 + +B_START_PACKED +struct requestproto_header { + uint32_t request_id; + uint32_t type; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/protocol/scproto.h b/external/badvpn_dns/protocol/scproto.h new file mode 100644 index 00000000..f138e0a5 --- /dev/null +++ b/external/badvpn_dns/protocol/scproto.h @@ -0,0 +1,266 @@ +/** + * @file scproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Definitions for SCProto, the protocol that the clients communicate in + * with the server. + * + * All multi-byte integers in structs are little-endian, unless stated otherwise. + * + * A SCProto packet consists of: + * - a header (struct {@link sc_header}) which contains the type of the + * packet + * - the payload + * + * It goes roughly like that: + * + * When the client connects to the server, it sends a "clienthello" packet + * to the server. The packet contains the protocol version the client is using. + * When the server receives the "clienthello" packet, it checks the version. + * If it doesn't match, it disconnects the client. Otherwise the server sends + * the client a "serverhello" packet to the client. That packet contains + * the ID of the client and possibly its IPv4 address as the server sees it + * (zero if not applicable). + * + * The server than proceeds to synchronize the peers' knowledge of each other. + * It does that by sending a "newclient" messages to a client to inform it of + * another peer, and "endclient" messages to inform it that a peer is gone. + * Each client, upon receiving a "newclient" message, MUST sent a corresponding + * "acceptpeer" message, before sending any messages to the new peer. + * The server forwards messages between synchronized peers to allow them to + * communicate. A peer sends a message to another peer by sending the "outmsg" + * packet to the server, and the server delivers a message to a peer by sending + * it the "inmsg" packet. + * + * The message service is reliable; messages from one client to another are + * expected to arrive unmodified and in the same order. There is, however, + * no flow control. This means that messages can not be used for bulk transfers + * between the clients (and they are not). If the server runs out of buffer for + * messages from one client to another, it will stop forwarding messages, and + * will reset knowledge of the two clients after some delay. Similarly, if one + * of the clients runs out of buffer locally, it will send the "resetpeer" + * packet to make the server reset knowledge. + * + * The messages transport either: + * + * - If the relevant "newclient" packets do not contain the + * SCID_NEWCLIENT_FLAG_SSL flag, then plaintext MsgProto messages. + * + * - If the relevant "newclient" packets do contain the SCID_NEWCLIENT_FLAG_SSL + * flag, then SSL, broken down into packets, PacketProto inside SSL, and finally + * MsgProto inside PacketProto. The master peer (one with higher ID) acts as an + * SSL server, and the other acts as an SSL client. The peers must identify with + * the same certificate they used when connecting to the server, and each peer + * must byte-compare the other's certificate agains the one provided to it by + * by the server in the relevent "newclient" message. + */ + +#ifndef BADVPN_PROTOCOL_SCPROTO_H +#define BADVPN_PROTOCOL_SCPROTO_H + +#include + +#include + +#define SC_VERSION 29 +#define SC_OLDVERSION_NOSSL 27 +#define SC_OLDVERSION_BROKENCERT 26 + +#define SC_KEEPALIVE_INTERVAL 10000 + +/** + * SCProto packet header. + * Follows up to SC_MAX_PAYLOAD bytes of payload. + */ +B_START_PACKED +struct sc_header { + /** + * Message type. + */ + uint8_t type; +} B_PACKED; +B_END_PACKED + +#define SC_MAX_PAYLOAD 2000 +#define SC_MAX_ENC (sizeof(struct sc_header) + SC_MAX_PAYLOAD) + +typedef uint16_t peerid_t; + +#define SCID_KEEPALIVE 0 +#define SCID_CLIENTHELLO 1 +#define SCID_SERVERHELLO 2 +#define SCID_NEWCLIENT 3 +#define SCID_ENDCLIENT 4 +#define SCID_OUTMSG 5 +#define SCID_INMSG 6 +#define SCID_RESETPEER 7 +#define SCID_ACCEPTPEER 8 + +/** + * "clienthello" client packet payload. + * Packet type is SCID_CLIENTHELLO. + */ +B_START_PACKED +struct sc_client_hello { + /** + * Protocol version the client is using. + */ + uint16_t version; +} B_PACKED; +B_END_PACKED + +/** + * "serverhello" server packet payload. + * Packet type is SCID_SERVERHELLO. + */ +B_START_PACKED +struct sc_server_hello { + /** + * Flags. Not used yet. + */ + uint16_t flags; + + /** + * Peer ID of the client. + */ + peerid_t id; + + /** + * IPv4 address of the client as seen by the server + * (network byte order). Zero if not applicable. + */ + uint32_t clientAddr; +} B_PACKED; +B_END_PACKED + +/** + * "newclient" server packet payload. + * Packet type is SCID_NEWCLIENT. + * If the server is using TLS, follows up to SCID_NEWCLIENT_MAX_CERT_LEN + * bytes of the new client's certificate (encoded in DER). + */ +B_START_PACKED +struct sc_server_newclient { + /** + * ID of the new peer. + */ + peerid_t id; + + /** + * Flags. Possible flags: + * - SCID_NEWCLIENT_FLAG_RELAY_SERVER + * You can relay frames to other peers through this peer. + * - SCID_NEWCLIENT_FLAG_RELAY_CLIENT + * You must allow this peer to relay frames to other peers through you. + * - SCID_NEWCLIENT_FLAG_SSL + * SSL must be used to talk to this peer through messages. + */ + uint16_t flags; +} B_PACKED; +B_END_PACKED + +#define SCID_NEWCLIENT_FLAG_RELAY_SERVER 1 +#define SCID_NEWCLIENT_FLAG_RELAY_CLIENT 2 +#define SCID_NEWCLIENT_FLAG_SSL 4 + +#define SCID_NEWCLIENT_MAX_CERT_LEN (SC_MAX_PAYLOAD - sizeof(struct sc_server_newclient)) + +/** + * "endclient" server packet payload. + * Packet type is SCID_ENDCLIENT. + */ +B_START_PACKED +struct sc_server_endclient { + /** + * ID of the removed peer. + */ + peerid_t id; +} B_PACKED; +B_END_PACKED + +/** + * "outmsg" client packet header. + * Packet type is SCID_OUTMSG. + * Follows up to SC_MAX_MSGLEN bytes of message payload. + */ +B_START_PACKED +struct sc_client_outmsg { + /** + * ID of the destionation peer. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +/** + * "inmsg" server packet payload. + * Packet type is SCID_INMSG. + * Follows up to SC_MAX_MSGLEN bytes of message payload. + */ +B_START_PACKED +struct sc_server_inmsg { + /** + * ID of the source peer. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +#define _SC_MAX_OUTMSGLEN (SC_MAX_PAYLOAD - sizeof(struct sc_client_outmsg)) +#define _SC_MAX_INMSGLEN (SC_MAX_PAYLOAD - sizeof(struct sc_server_inmsg)) + +#define SC_MAX_MSGLEN (_SC_MAX_OUTMSGLEN < _SC_MAX_INMSGLEN ? _SC_MAX_OUTMSGLEN : _SC_MAX_INMSGLEN) + +/** + * "resetpeer" client packet header. + * Packet type is SCID_RESETPEER. + */ +B_START_PACKED +struct sc_client_resetpeer { + /** + * ID of the peer to reset. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +/** + * "acceptpeer" client packet payload. + * Packet type is SCID_ACCEPTPEER. + */ +B_START_PACKED +struct sc_client_acceptpeer { + /** + * ID of the peer to accept. + */ + peerid_t clientid; +} B_PACKED; +B_END_PACKED + +#endif diff --git a/external/badvpn_dns/protocol/spproto.h b/external/badvpn_dns/protocol/spproto.h new file mode 100644 index 00000000..4b5bf5dc --- /dev/null +++ b/external/badvpn_dns/protocol/spproto.h @@ -0,0 +1,195 @@ +/** + * @file spproto.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Protocol for securing datagram communication. + * + * Security features implemented: + * - Encryption. Encrypts packets with a block cipher. + * Protects against a third party from seeing the data + * being transmitted. + * - Hashes. Adds a hash of the packet into the packet. + * Combined with encryption, protects against tampering + * with packets and crafting new packets. + * - One-time passwords. Adds a password to each packet + * for the receiver to recognize. Protects agains replaying + * packets and crafting new packets. + * + * A SPProto plaintext packet contains the following, in order: + * - if OTPs are used, a struct {@link spproto_otpdata} which contains + * the seed ID and the OTP, + * - if hashes are used, the hash, + * - payload data. + * + * If encryption is used: + * - the plaintext is padded by appending a 0x01 byte and as many 0x00 + * bytes as needed to align to block size, + * - the padded plaintext is encrypted, and + * - the initialization vector (IV) is prepended. + */ + +#ifndef BADVPN_PROTOCOL_SPPROTO_H +#define BADVPN_PROTOCOL_SPPROTO_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define SPPROTO_HASH_MODE_NONE 0 +#define SPPROTO_ENCRYPTION_MODE_NONE 0 +#define SPPROTO_OTP_MODE_NONE 0 + +/** + * Stores security parameters for SPProto. + */ +struct spproto_security_params { + /** + * Hash mode. + * Either SPPROTO_HASH_MODE_NONE for no hashes, or a valid bhash + * hash mode. + */ + int hash_mode; + + /** + * Encryption mode. + * Either SPPROTO_ENCRYPTION_MODE_NONE for no encryption, or a valid + * {@link BEncryption} cipher. + */ + int encryption_mode; + + /** + * One-time password (OTP) mode. + * Either SPPROTO_OTP_MODE_NONE for no OTPs, or a valid + * {@link BEncryption} cipher. + */ + int otp_mode; + + /** + * If OTPs are used (otp_mode != SPPROTO_OTP_MODE_NONE), number of + * OTPs generated from a single seed. + */ + int otp_num; +}; + +#define SPPROTO_HAVE_HASH(_params) ((_params).hash_mode != SPPROTO_HASH_MODE_NONE) +#define SPPROTO_HASH_SIZE(_params) ( \ + SPPROTO_HAVE_HASH(_params) ? \ + BHash_size((_params).hash_mode) : \ + 0 \ +) + +#define SPPROTO_HAVE_ENCRYPTION(_params) ((_params).encryption_mode != SPPROTO_ENCRYPTION_MODE_NONE) + +#define SPPROTO_HAVE_OTP(_params) ((_params).otp_mode != SPPROTO_OTP_MODE_NONE) + +B_START_PACKED +struct spproto_otpdata { + uint16_t seed_id; + otp_t otp; +} B_PACKED; +B_END_PACKED + +#define SPPROTO_HEADER_OTPDATA_OFF(_params) 0 +#define SPPROTO_HEADER_OTPDATA_LEN(_params) (SPPROTO_HAVE_OTP(_params) ? sizeof(struct spproto_otpdata) : 0) +#define SPPROTO_HEADER_HASH_OFF(_params) (SPPROTO_HEADER_OTPDATA_OFF(_params) + SPPROTO_HEADER_OTPDATA_LEN(_params)) +#define SPPROTO_HEADER_HASH_LEN(_params) SPPROTO_HASH_SIZE(_params) +#define SPPROTO_HEADER_LEN(_params) (SPPROTO_HEADER_HASH_OFF(_params) + SPPROTO_HEADER_HASH_LEN(_params)) + +/** + * Asserts that the given SPProto security parameters are valid. + * + * @param params security parameters + */ +static void spproto_assert_security_params (struct spproto_security_params params) +{ + ASSERT(params.hash_mode == SPPROTO_HASH_MODE_NONE || BHash_type_valid(params.hash_mode)) + ASSERT(params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE || BEncryption_cipher_valid(params.encryption_mode)) + ASSERT(params.otp_mode == SPPROTO_OTP_MODE_NONE || BEncryption_cipher_valid(params.otp_mode)) + ASSERT(params.otp_mode == SPPROTO_OTP_MODE_NONE || params.otp_num > 0) +} + +/** + * Calculates the maximum payload size for SPProto given the + * security parameters and the maximum encoded packet size. + * + * @param params security parameters + * @param carrier_mtu maximum encoded packet size. Must be >=0. + * @return maximum payload size. Negative means is is impossible + * to encode anything. + */ +static int spproto_payload_mtu_for_carrier_mtu (struct spproto_security_params params, int carrier_mtu) +{ + spproto_assert_security_params(params); + ASSERT(carrier_mtu >= 0) + + if (params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE) { + return (carrier_mtu - SPPROTO_HEADER_LEN(params)); + } else { + int block_size = BEncryption_cipher_block_size(params.encryption_mode); + return (balign_down(carrier_mtu, block_size) - block_size - SPPROTO_HEADER_LEN(params) - 1); + } +} + +/** + * Calculates the maximum encoded packet size for SPProto given the + * security parameters and the maximum payload size. + * + * @param params security parameters + * @param payload_mtu maximum payload size. Must be >=0. + * @return maximum encoded packet size, -1 if payload_mtu is too large + */ +static int spproto_carrier_mtu_for_payload_mtu (struct spproto_security_params params, int payload_mtu) +{ + spproto_assert_security_params(params); + ASSERT(payload_mtu >= 0) + + if (params.encryption_mode == SPPROTO_ENCRYPTION_MODE_NONE) { + if (payload_mtu > INT_MAX - SPPROTO_HEADER_LEN(params)) { + return -1; + } + + return (SPPROTO_HEADER_LEN(params) + payload_mtu); + } else { + int block_size = BEncryption_cipher_block_size(params.encryption_mode); + + if (payload_mtu > INT_MAX - (block_size + SPPROTO_HEADER_LEN(params) + block_size)) { + return -1; + } + + return (block_size + balign_up((SPPROTO_HEADER_LEN(params) + payload_mtu + 1), block_size)); + } +} + +#endif diff --git a/external/badvpn_dns/protocol/udpgw_proto.h b/external/badvpn_dns/protocol/udpgw_proto.h new file mode 100644 index 00000000..606fe6c4 --- /dev/null +++ b/external/badvpn_dns/protocol/udpgw_proto.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_PROTOCOL_UDPGW_PROTO_H +#define BADVPN_PROTOCOL_UDPGW_PROTO_H + +#include + +#include +#include + +#define UDPGW_CLIENT_FLAG_KEEPALIVE (1 << 0) +#define UDPGW_CLIENT_FLAG_REBIND (1 << 1) +#define UDPGW_CLIENT_FLAG_DNS (1 << 2) +#define UDPGW_CLIENT_FLAG_IPV6 (1 << 3) + +B_START_PACKED +struct udpgw_header { + uint8_t flags; + uint16_t conid; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv4 { + uint32_t addr_ip; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct udpgw_addr_ipv6 { + uint8_t addr_ip[16]; + uint16_t addr_port; +} B_PACKED; +B_END_PACKED + +static int udpgw_compute_mtu (int dgram_mtu) +{ + bsize_t bs = bsize_add( + bsize_fromsize(sizeof(struct udpgw_header)), + bsize_add( + bsize_max( + bsize_fromsize(sizeof(struct udpgw_addr_ipv4)), + bsize_fromsize(sizeof(struct udpgw_addr_ipv6)) + ), + bsize_fromint(dgram_mtu) + ) + ); + + int s; + if (!bsize_toint(bs, &s)) { + return -1; + } + + return s; +} + +#endif diff --git a/external/badvpn_dns/random/BRandom2.c b/external/badvpn_dns/random/BRandom2.c new file mode 100644 index 00000000..a0761de4 --- /dev/null +++ b/external/badvpn_dns/random/BRandom2.c @@ -0,0 +1,90 @@ +/** + * @file BRandom2.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "BRandom2.h" + +static int do_init (BRandom2 *o) +{ + if (o->initialized) { + return 1; + } + + o->urandom_fd = open("/dev/urandom", O_RDONLY); + if (o->urandom_fd < 0) { + return 0; + } + + o->initialized = 1; + + return 1; +} + +int BRandom2_Init (BRandom2 *o, int flags) +{ + ASSERT((flags & ~(BRANDOM2_INIT_LAZY)) == 0) + + o->initialized = 0; + + if (!(flags & BRANDOM2_INIT_LAZY) && !do_init(o)) { + return 0; + } + + DebugObject_Init(&o->d_obj); + return 1; +} + +void BRandom2_Free (BRandom2 *o) +{ + DebugObject_Free(&o->d_obj); + + if (o->initialized) { + close(o->urandom_fd); + } +} + +int BRandom2_GenBytes (BRandom2 *o, void *out, size_t len) +{ + DebugObject_Access(&o->d_obj); + + if (!do_init(o)) { + return 0; + } + + ssize_t res = read(o->urandom_fd, out, len); + if (res < 0 || res != len) { + return 0; + } + + return 1; +} diff --git a/external/badvpn_dns/random/BRandom2.h b/external/badvpn_dns/random/BRandom2.h new file mode 100644 index 00000000..6851b842 --- /dev/null +++ b/external/badvpn_dns/random/BRandom2.h @@ -0,0 +1,50 @@ +/** + * @file BRandom2.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_RANDOM2_H +#define BADVPN_RANDOM2_H + +#include + +#include +#include + +#define BRANDOM2_INIT_LAZY (1 << 0) + +typedef struct { + int initialized; + int urandom_fd; + DebugObject d_obj; +} BRandom2; + +int BRandom2_Init (BRandom2 *o, int flags) WARN_UNUSED; +void BRandom2_Free (BRandom2 *o); +int BRandom2_GenBytes (BRandom2 *o, void *out, size_t len) WARN_UNUSED; + +#endif diff --git a/external/badvpn_dns/random/CMakeLists.txt b/external/badvpn_dns/random/CMakeLists.txt new file mode 100644 index 00000000..76a48214 --- /dev/null +++ b/external/badvpn_dns/random/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(badvpn_random "base" "" BRandom2.c) diff --git a/external/badvpn_dns/scripts/cmake b/external/badvpn_dns/scripts/cmake new file mode 100755 index 00000000..51af7777 --- /dev/null +++ b/external/badvpn_dns/scripts/cmake @@ -0,0 +1,8 @@ +#!/bin/sh + +export ROOT="" +export MINGW="/home//mingw/cross_win32" + +export PATH="$MINGW/bin:$PATH" + +exec /usr/bin/cmake -DCMAKE_TOOLCHAIN_FILE="$ROOT/toolchain.cmake" "$@" diff --git a/external/badvpn_dns/scripts/copy_nss b/external/badvpn_dns/scripts/copy_nss new file mode 100755 index 00000000..9b521120 --- /dev/null +++ b/external/badvpn_dns/scripts/copy_nss @@ -0,0 +1,23 @@ +#!/bin/sh + +NSSDIST="$1" +DEST="$2" + +if [ -z "${NSSDIST}" ] || [ -z "${DEST}" ]; then + echo "Copies a Windows build of NSS such that it can be found by the BadVPN build system" + echo "Usage: $0 " + exit 1 +fi + +NSSOBJ="${NSSDIST}/WINNT5.1_OPT.OBJ" + +set -e + +mkdir -p "${DEST}"/include +cp -r "${NSSOBJ}/include"/* "${DEST}"/include/ +cp -r "${NSSDIST}/public/nss"/* "${DEST}"/include/ +mkdir -p "${DEST}"/lib +cp "${NSSOBJ}/lib"/{libnspr4,libplc4,libplds4,ssl3,smime3,nss3}.lib "${DEST}"/lib/ +mkdir -p "${DEST}"/bin +cp "${NSSOBJ}/lib"/*.dll "${DEST}"/bin/ +cp "${NSSOBJ}/bin"/*.exe "${DEST}"/bin/ diff --git a/external/badvpn_dns/scripts/toolchain.cmake b/external/badvpn_dns/scripts/toolchain.cmake new file mode 100644 index 00000000..5f4a90a6 --- /dev/null +++ b/external/badvpn_dns/scripts/toolchain.cmake @@ -0,0 +1,6 @@ +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +SET(CMAKE_FIND_ROOT_PATH $ENV{ROOT}) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/external/badvpn_dns/security/BEncryption.c b/external/badvpn_dns/security/BEncryption.c new file mode 100644 index 00000000..f0f476ca --- /dev/null +++ b/external/badvpn_dns/security/BEncryption.c @@ -0,0 +1,240 @@ +/** + * @file BEncryption.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +int BEncryption_cipher_valid (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + case BENCRYPTION_CIPHER_AES: + return 1; + default: + return 0; + } +} + +int BEncryption_cipher_block_size (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + return BENCRYPTION_CIPHER_BLOWFISH_BLOCK_SIZE; + case BENCRYPTION_CIPHER_AES: + return BENCRYPTION_CIPHER_AES_BLOCK_SIZE; + default: + ASSERT(0) + return 0; + } +} + +int BEncryption_cipher_key_size (int cipher) +{ + switch (cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + return BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE; + case BENCRYPTION_CIPHER_AES: + return BENCRYPTION_CIPHER_AES_KEY_SIZE; + default: + ASSERT(0) + return 0; + } +} + +void BEncryption_Init (BEncryption *enc, int mode, int cipher, uint8_t *key) +{ + ASSERT(!(mode&~(BENCRYPTION_MODE_ENCRYPT|BENCRYPTION_MODE_DECRYPT))) + ASSERT((mode&BENCRYPTION_MODE_ENCRYPT) || (mode&BENCRYPTION_MODE_DECRYPT)) + + enc->mode = mode; + enc->cipher = cipher; + + #ifdef BADVPN_USE_CRYPTODEV + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_AES: + enc->cryptodev.cipher = CRYPTO_AES_CBC; + break; + default: + goto fail1; + } + + if ((enc->cryptodev.fd = open("/dev/crypto", O_RDWR, 0)) < 0) { + BLog(BLOG_ERROR, "failed to open /dev/crypto"); + goto fail1; + } + + if (ioctl(enc->cryptodev.fd, CRIOGET, &enc->cryptodev.cfd)) { + BLog(BLOG_ERROR, "failed ioctl(CRIOGET)"); + goto fail2; + } + + struct session_op sess; + memset(&sess, 0, sizeof(sess)); + sess.cipher = enc->cryptodev.cipher; + sess.keylen = BEncryption_cipher_key_size(enc->cipher); + sess.key = key; + if (ioctl(enc->cryptodev.cfd, CIOCGSESSION, &sess)) { + BLog(BLOG_ERROR, "failed ioctl(CIOCGSESSION)"); + goto fail3; + } + + enc->cryptodev.ses = sess.ses; + enc->use_cryptodev = 1; + + goto success; + +fail3: + ASSERT_FORCE(close(enc->cryptodev.cfd) == 0) +fail2: + ASSERT_FORCE(close(enc->cryptodev.fd) == 0) +fail1: + + enc->use_cryptodev = 0; + + #endif + + int res; + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_set_key(&enc->blowfish, BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE, key); + break; + case BENCRYPTION_CIPHER_AES: + if (enc->mode&BENCRYPTION_MODE_ENCRYPT) { + res = AES_set_encrypt_key(key, 128, &enc->aes.encrypt); + ASSERT_EXECUTE(res >= 0) + } + if (enc->mode&BENCRYPTION_MODE_DECRYPT) { + res = AES_set_decrypt_key(key, 128, &enc->aes.decrypt); + ASSERT_EXECUTE(res >= 0) + } + break; + default: + ASSERT(0) + ; + } + + #ifdef BADVPN_USE_CRYPTODEV +success: + #endif + // init debug object + DebugObject_Init(&enc->d_obj); +} + +void BEncryption_Free (BEncryption *enc) +{ + // free debug object + DebugObject_Free(&enc->d_obj); + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCFSESSION, &enc->cryptodev.ses) == 0) + ASSERT_FORCE(close(enc->cryptodev.cfd) == 0) + ASSERT_FORCE(close(enc->cryptodev.fd) == 0) + } + + #endif +} + +void BEncryption_Encrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv) +{ + ASSERT(enc->mode&BENCRYPTION_MODE_ENCRYPT) + ASSERT(len >= 0) + ASSERT(len % BEncryption_cipher_block_size(enc->cipher) == 0) + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + struct crypt_op cryp; + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = enc->cryptodev.ses; + cryp.len = len; + cryp.src = in; + cryp.dst = out; + cryp.iv = iv; + cryp.op = COP_ENCRYPT; + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCCRYPT, &cryp) == 0) + + return; + } + + #endif + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_cbc_encrypt(in, out, len, &enc->blowfish, iv, BF_ENCRYPT); + break; + case BENCRYPTION_CIPHER_AES: + AES_cbc_encrypt(in, out, len, &enc->aes.encrypt, iv, AES_ENCRYPT); + break; + default: + ASSERT(0); + } +} + +void BEncryption_Decrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv) +{ + ASSERT(enc->mode&BENCRYPTION_MODE_DECRYPT) + ASSERT(len >= 0) + ASSERT(len % BEncryption_cipher_block_size(enc->cipher) == 0) + + #ifdef BADVPN_USE_CRYPTODEV + + if (enc->use_cryptodev) { + struct crypt_op cryp; + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = enc->cryptodev.ses; + cryp.len = len; + cryp.src = in; + cryp.dst = out; + cryp.iv = iv; + cryp.op = COP_DECRYPT; + ASSERT_FORCE(ioctl(enc->cryptodev.cfd, CIOCCRYPT, &cryp) == 0) + + return; + } + + #endif + + switch (enc->cipher) { + case BENCRYPTION_CIPHER_BLOWFISH: + BF_cbc_encrypt(in, out, len, &enc->blowfish, iv, BF_DECRYPT); + break; + case BENCRYPTION_CIPHER_AES: + AES_cbc_encrypt(in, out, len, &enc->aes.decrypt, iv, AES_DECRYPT); + break; + default: + ASSERT(0); + } +} diff --git a/external/badvpn_dns/security/BEncryption.h b/external/badvpn_dns/security/BEncryption.h new file mode 100644 index 00000000..60a4fdcc --- /dev/null +++ b/external/badvpn_dns/security/BEncryption.h @@ -0,0 +1,175 @@ +/** + * @file BEncryption.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Block cipher encryption abstraction. + */ + +#ifndef BADVPN_SECURITY_BENCRYPTION_H +#define BADVPN_SECURITY_BENCRYPTION_H + +#include +#include + +#ifdef BADVPN_USE_CRYPTODEV +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#define BENCRYPTION_MODE_ENCRYPT 1 +#define BENCRYPTION_MODE_DECRYPT 2 + +#define BENCRYPTION_MAX_BLOCK_SIZE 16 +#define BENCRYPTION_MAX_KEY_SIZE 16 + +#define BENCRYPTION_CIPHER_BLOWFISH 1 +#define BENCRYPTION_CIPHER_BLOWFISH_BLOCK_SIZE 8 +#define BENCRYPTION_CIPHER_BLOWFISH_KEY_SIZE 16 + +#define BENCRYPTION_CIPHER_AES 2 +#define BENCRYPTION_CIPHER_AES_BLOCK_SIZE 16 +#define BENCRYPTION_CIPHER_AES_KEY_SIZE 16 + +// NOTE: update the maximums above when adding a cipher! + +/** + * Block cipher encryption abstraction. + */ +typedef struct { + DebugObject d_obj; + int mode; + int cipher; + #ifdef BADVPN_USE_CRYPTODEV + int use_cryptodev; + #endif + union { + BF_KEY blowfish; + struct { + AES_KEY encrypt; + AES_KEY decrypt; + } aes; + #ifdef BADVPN_USE_CRYPTODEV + struct { + int fd; + int cfd; + int cipher; + uint32_t ses; + } cryptodev; + #endif + }; +} BEncryption; + +/** + * Checks if the given cipher number is valid. + * + * @param cipher cipher number + * @return 1 if valid, 0 if not + */ +int BEncryption_cipher_valid (int cipher); + +/** + * Returns the block size of a cipher. + * + * @param cipher cipher number. Must be valid. + * @return block size in bytes + */ +int BEncryption_cipher_block_size (int cipher); + +/** + * Returns the key size of a cipher. + * + * @param cipher cipher number. Must be valid. + * @return key size in bytes + */ +int BEncryption_cipher_key_size (int cipher); + +/** + * Initializes the object. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this object + * will be used from a non-main thread. + * + * @param enc the object + * @param mode whether encryption or decryption is to be done, or both. + * Must be a bitwise-OR of at least one of BENCRYPTION_MODE_ENCRYPT + * and BENCRYPTION_MODE_DECRYPT. + * @param cipher cipher number. Must be valid. + * @param key encryption key + */ +void BEncryption_Init (BEncryption *enc, int mode, int cipher, uint8_t *key); + +/** + * Frees the object. + * + * @param enc the object + */ +void BEncryption_Free (BEncryption *enc); + +/** + * Encrypts data. + * The object must have been initialized with mode including + * BENCRYPTION_MODE_ENCRYPT. + * + * @param enc the object + * @param in data to encrypt + * @param out ciphertext output + * @param len number of bytes to encrypt. Must be >=0 and a multiple of + * block size. + * @param iv initialization vector. Updated such that continuing a previous encryption + * starting with the updated IV is equivalent to performing just one encryption. + */ +void BEncryption_Encrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv); + +/** + * Decrypts data. + * The object must have been initialized with mode including + * BENCRYPTION_MODE_DECRYPT. + * + * @param enc the object + * @param in data to decrypt + * @param out plaintext output + * @param len number of bytes to decrypt. Must be >=0 and a multiple of + * block size. + * @param iv initialization vector. Updated such that continuing a previous decryption + * starting with the updated IV is equivalent to performing just one decryption. + */ +void BEncryption_Decrypt (BEncryption *enc, uint8_t *in, uint8_t *out, int len, uint8_t *iv); + +#endif diff --git a/external/badvpn_dns/security/BHash.c b/external/badvpn_dns/security/BHash.c new file mode 100644 index 00000000..fec26165 --- /dev/null +++ b/external/badvpn_dns/security/BHash.c @@ -0,0 +1,69 @@ +/** + * @file BHash.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int BHash_type_valid (int type) +{ + switch (type) { + case BHASH_TYPE_MD5: + case BHASH_TYPE_SHA1: + return 1; + default: + return 0; + } +} + +int BHash_size (int type) +{ + switch (type) { + case BHASH_TYPE_MD5: + return BHASH_TYPE_MD5_SIZE; + case BHASH_TYPE_SHA1: + return BHASH_TYPE_SHA1_SIZE; + default: + ASSERT(0) + return 0; + } +} + +void BHash_calculate (int type, uint8_t *data, int data_len, uint8_t *out) +{ + switch (type) { + case BHASH_TYPE_MD5: + MD5(data, data_len, out); + break; + case BHASH_TYPE_SHA1: + SHA1(data, data_len, out); + break; + default: + ASSERT(0) + ; + } +} diff --git a/external/badvpn_dns/security/BHash.h b/external/badvpn_dns/security/BHash.h new file mode 100644 index 00000000..6fb6f9a1 --- /dev/null +++ b/external/badvpn_dns/security/BHash.h @@ -0,0 +1,80 @@ +/** + * @file BHash.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Cryptographic hash funtions abstraction. + */ + +#ifndef BADVPN_SECURITY_BHASH_H +#define BADVPN_SECURITY_BHASH_H + +#include + +#include +#include + +#include + +#define BHASH_TYPE_MD5 1 +#define BHASH_TYPE_MD5_SIZE 16 + +#define BHASH_TYPE_SHA1 2 +#define BHASH_TYPE_SHA1_SIZE 20 + +#define BHASH_MAX_SIZE 20 + +/** + * Checks if the given hash type number is valid. + * + * @param type hash type number + * @return 1 if valid, 0 if not + */ +int BHash_type_valid (int type); + +/** + * Returns the size of a hash. + * + * @param cipher hash type number. Must be valid. + * @return hash size in bytes + */ +int BHash_size (int type); + +/** + * Calculates a hash. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this is + * being called from a non-main thread. + * + * @param type hash type number. Must be valid. + * @param data data to calculate the hash of + * @param data_len length of data + * @param out the hash will be written here. Must not overlap with data. + */ +void BHash_calculate (int type, uint8_t *data, int data_len, uint8_t *out); + +#endif diff --git a/external/badvpn_dns/security/BRandom.c b/external/badvpn_dns/security/BRandom.c new file mode 100644 index 00000000..ea9f2761 --- /dev/null +++ b/external/badvpn_dns/security/BRandom.c @@ -0,0 +1,42 @@ +/** + * @file BRandom.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +void BRandom_randomize (uint8_t *buf, int len) +{ + ASSERT(len >= 0) + + DEBUG_ZERO_MEMORY(buf, len) + ASSERT_FORCE(RAND_bytes(buf, len) == 1) +} diff --git a/external/badvpn_dns/security/BRandom.h b/external/badvpn_dns/security/BRandom.h new file mode 100644 index 00000000..3529b828 --- /dev/null +++ b/external/badvpn_dns/security/BRandom.h @@ -0,0 +1,49 @@ +/** + * @file BRandom.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Random data generation function. + */ + +#ifndef BADVPN_SECURITY_BRANDOM_H +#define BADVPN_SECURITY_BRANDOM_H + +#include + +/** + * Generates random data. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this is + * being called from a non-main thread. + * + * @param buf buffer to write data into + * @param len number of bytes to generate. Must be >=0. + */ +void BRandom_randomize (uint8_t *buf, int len); + +#endif diff --git a/external/badvpn_dns/security/BSecurity.c b/external/badvpn_dns/security/BSecurity.c new file mode 100644 index 00000000..f3fb7c88 --- /dev/null +++ b/external/badvpn_dns/security/BSecurity.c @@ -0,0 +1,149 @@ +/** + * @file BSecurity.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include +#endif + +#include + +#include +#include + +#include + +int bsecurity_initialized = 0; + +#ifdef BADVPN_THREADWORK_USE_PTHREAD +pthread_mutex_t *bsecurity_locks; +int bsecurity_num_locks; +#endif + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + +static unsigned long id_callback (void) +{ + ASSERT(bsecurity_initialized) + + return (unsigned long)pthread_self(); +} + +static void locking_callback (int mode, int type, const char *file, int line) +{ + ASSERT(bsecurity_initialized) + ASSERT(type >= 0) + ASSERT(type < bsecurity_num_locks) + + if ((mode & CRYPTO_LOCK)) { + ASSERT_FORCE(pthread_mutex_lock(&bsecurity_locks[type]) == 0) + } else { + ASSERT_FORCE(pthread_mutex_unlock(&bsecurity_locks[type]) == 0) + } +} + +#endif + +int BSecurity_GlobalInitThreadSafe (void) +{ + ASSERT(!bsecurity_initialized) + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + // get number of locks + int num_locks = CRYPTO_num_locks(); + ASSERT_FORCE(num_locks >= 0) + + // alloc locks array + if (!(bsecurity_locks = BAllocArray(num_locks, sizeof(bsecurity_locks[0])))) { + goto fail0; + } + + // init locks + bsecurity_num_locks = 0; + for (int i = 0; i < num_locks; i++) { + if (pthread_mutex_init(&bsecurity_locks[i], NULL) != 0) { + goto fail1; + } + bsecurity_num_locks++; + } + + #endif + + bsecurity_initialized = 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + CRYPTO_set_id_callback(id_callback); + CRYPTO_set_locking_callback(locking_callback); + #endif + + return 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD +fail1: + while (bsecurity_num_locks > 0) { + ASSERT_FORCE(pthread_mutex_destroy(&bsecurity_locks[bsecurity_num_locks - 1]) == 0) + bsecurity_num_locks--; + } + BFree(bsecurity_locks); +fail0: + return 0; + #endif +} + +void BSecurity_GlobalFreeThreadSafe (void) +{ + ASSERT(bsecurity_initialized) + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + // remove callbacks + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + // free locks + while (bsecurity_num_locks > 0) { + ASSERT_FORCE(pthread_mutex_destroy(&bsecurity_locks[bsecurity_num_locks - 1]) == 0) + bsecurity_num_locks--; + } + + // free locks array + BFree(bsecurity_locks); + + #endif + + bsecurity_initialized = 0; +} + +void BSecurity_GlobalAssertThreadSafe (int thread_safe) +{ + ASSERT(thread_safe == 0 || thread_safe == 1) + ASSERT(!(thread_safe) || bsecurity_initialized) +} diff --git a/external/badvpn_dns/security/BSecurity.h b/external/badvpn_dns/security/BSecurity.h new file mode 100644 index 00000000..c429e4e6 --- /dev/null +++ b/external/badvpn_dns/security/BSecurity.h @@ -0,0 +1,60 @@ +/** + * @file BSecurity.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Initialization of OpenSSL for security functions. + */ + +#ifndef BADVPN_SECURITY_BSECURITY_H +#define BADVPN_SECURITY_BSECURITY_H + +/** + * Initializes thread safety for security functions. + * Thread safety must not be initialized. + * + * @return 1 on success, 0 on failure + */ +int BSecurity_GlobalInitThreadSafe (void); + +/** + * Deinitializes thread safety for security functions. + * Thread safety must be initialized. + */ +void BSecurity_GlobalFreeThreadSafe (void); + +/** + * Asserts that {@link BSecurity_GlobalInitThreadSafe} was done, + * if thread_safe=1. + * + * @param thread_safe whether thread safety is to be asserted. + * Must be 0 or 1. + */ +void BSecurity_GlobalAssertThreadSafe (int thread_safe); + +#endif diff --git a/external/badvpn_dns/security/CMakeLists.txt b/external/badvpn_dns/security/CMakeLists.txt new file mode 100644 index 00000000..fe29a516 --- /dev/null +++ b/external/badvpn_dns/security/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SECURITY_SOURCES + BSecurity.c + BEncryption.c + BHash.c + BRandom.c + OTPCalculator.c + OTPChecker.c + OTPGenerator.c +) +badvpn_add_library(security "system;threadwork" "${LIBCRYPTO_LIBRARIES}" "${SECURITY_SOURCES}") diff --git a/external/badvpn_dns/security/OTPCalculator.c b/external/badvpn_dns/security/OTPCalculator.c new file mode 100644 index 00000000..069dbe6b --- /dev/null +++ b/external/badvpn_dns/security/OTPCalculator.c @@ -0,0 +1,118 @@ +/** + * @file OTPCalculator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +int OTPCalculator_Init (OTPCalculator *calc, int num_otps, int cipher) +{ + ASSERT(num_otps >= 0) + ASSERT(BEncryption_cipher_valid(cipher)) + + // init arguments + calc->num_otps = num_otps; + calc->cipher = cipher; + + // remember block size + calc->block_size = BEncryption_cipher_block_size(calc->cipher); + + // calculate number of blocks + if (calc->num_otps > SIZE_MAX / sizeof(otp_t)) { + goto fail0; + } + calc->num_blocks = bdivide_up(calc->num_otps * sizeof(otp_t), calc->block_size); + + // allocate buffer + if (!(calc->data = (otp_t *)BAllocArray(calc->num_blocks, calc->block_size))) { + goto fail0; + } + + // init debug object + DebugObject_Init(&calc->d_obj); + + return 1; + +fail0: + return 0; +} + +void OTPCalculator_Free (OTPCalculator *calc) +{ + // free debug object + DebugObject_Free(&calc->d_obj); + + // free buffer + BFree(calc->data); +} + +otp_t * OTPCalculator_Generate (OTPCalculator *calc, uint8_t *key, uint8_t *iv, int shuffle) +{ + ASSERT(shuffle == 0 || shuffle == 1) + + // copy IV so it can be updated + uint8_t iv_work[BENCRYPTION_MAX_BLOCK_SIZE]; + memcpy(iv_work, iv, calc->block_size); + + // create zero block + uint8_t zero[BENCRYPTION_MAX_BLOCK_SIZE]; + memset(zero, 0, calc->block_size); + + // init encryptor + BEncryption encryptor; + BEncryption_Init(&encryptor, BENCRYPTION_MODE_ENCRYPT, calc->cipher, key); + + // encrypt zero blocks + for (size_t i = 0; i < calc->num_blocks; i++) { + BEncryption_Encrypt(&encryptor, zero, (uint8_t *)calc->data + i * calc->block_size, calc->block_size, iv_work); + } + + // free encryptor + BEncryption_Free(&encryptor); + + // shuffle if requested + if (shuffle) { + int i = 0; + while (i < calc->num_otps) { + uint16_t ints[256]; + BRandom_randomize((uint8_t *)ints, sizeof(ints)); + for (int j = 0; j < 256 && i < calc->num_otps; j++) { + int newIndex = i + (ints[j] % (calc->num_otps - i)); + otp_t temp = calc->data[i]; + calc->data[i] = calc->data[newIndex]; + calc->data[newIndex] = temp; + i++; + } + } + } + + return calc->data; +} diff --git a/external/badvpn_dns/security/OTPCalculator.h b/external/badvpn_dns/security/OTPCalculator.h new file mode 100644 index 00000000..f15f845f --- /dev/null +++ b/external/badvpn_dns/security/OTPCalculator.h @@ -0,0 +1,96 @@ +/** + * @file OTPCalculator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that calculates OTPs. + */ + +#ifndef BADVPN_SECURITY_OTPCALCULATOR_H +#define BADVPN_SECURITY_OTPCALCULATOR_H + +#include +#include + +#include +#include +#include +#include +#include + +/** + * Type for an OTP. + */ +typedef uint32_t otp_t; + +/** + * Object that calculates OTPs. + */ +typedef struct { + DebugObject d_obj; + int num_otps; + int cipher; + int block_size; + size_t num_blocks; + otp_t *data; +} OTPCalculator; + +/** + * Initializes the calculator. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if this object + * will be used from a non-main thread. + * + * @param calc the object + * @param num_otps number of OTPs to generate from a seed. Must be >=0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @return 1 on success, 0 on failure + */ +int OTPCalculator_Init (OTPCalculator *calc, int num_otps, int cipher) WARN_UNUSED; + +/** + * Frees the calculator. + * + * @param calc the object + */ +void OTPCalculator_Free (OTPCalculator *calc); + +/** + * Generates OTPs from the given key and IV. + * + * @param calc the object + * @param key encryption key + * @param iv initialization vector + * @param shuffle whether to shuffle the OTPs. Must be 1 or 0. + * @return pointer to an array of 32-bit OPTs. Constains as many OTPs as was specified + * in {@link OTPCalculator_Init}. Valid until the next generation or + * until the object is freed. + */ +otp_t * OTPCalculator_Generate (OTPCalculator *calc, uint8_t *key, uint8_t *iv, int shuffle); + +#endif diff --git a/external/badvpn_dns/security/OTPChecker.c b/external/badvpn_dns/security/OTPChecker.c new file mode 100644 index 00000000..8606a316 --- /dev/null +++ b/external/badvpn_dns/security/OTPChecker.c @@ -0,0 +1,297 @@ +/** + * @file OTPChecker.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include + +static void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t); +static void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp); +static void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv); +static int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp); + +void OTPChecker_Table_Empty (OTPChecker *mc, struct OTPChecker_table *t) +{ + for (int i = 0; i < mc->num_entries; i++) { + t->entries[i].avail = -1; + } +} + +void OTPChecker_Table_AddOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp) +{ + // calculate starting index + int start_index = otp % mc->num_entries; + + // try indexes starting with the base position + for (int i = 0; i < mc->num_entries; i++) { + int index = bmodadd_int(start_index, i, mc->num_entries); + struct OTPChecker_entry *entry = &t->entries[index]; + + // if we find a free index, use it + if (entry->avail < 0) { + entry->otp = otp; + entry->avail = 1; + return; + } + + // if we find a used index with the same mac, + // use it by incrementing its count + if (entry->otp == otp) { + entry->avail++; + return; + } + } + + // will never add more macs than we can hold + ASSERT(0) +} + +void OTPChecker_Table_Generate (OTPChecker *mc, struct OTPChecker_table *t, OTPCalculator *calc, uint8_t *key, uint8_t *iv) +{ + // calculate values + otp_t *otps = OTPCalculator_Generate(calc, key, iv, 0); + + // empty table + OTPChecker_Table_Empty(mc ,t); + + // add calculated values to table + for (int i = 0; i < mc->num_otps; i++) { + OTPChecker_Table_AddOTP(mc, t, otps[i]); + } +} + +int OTPChecker_Table_CheckOTP (OTPChecker *mc, struct OTPChecker_table *t, otp_t otp) +{ + // calculate starting index + int start_index = otp % mc->num_entries; + + // try indexes starting with the base position + for (int i = 0; i < mc->num_entries; i++) { + int index = bmodadd_int(start_index, i, mc->num_entries); + struct OTPChecker_entry *entry = &t->entries[index]; + + // if we find an empty entry, there is no such mac + if (entry->avail < 0) { + return 0; + } + + // if we find a matching entry, check its count + if (entry->otp == otp) { + if (entry->avail > 0) { + entry->avail--; + return 1; + } + return 0; + } + } + + // there are always empty slots + ASSERT(0) + return 0; +} + +static void work_func (OTPChecker *mc) +{ + struct OTPChecker_table *table = &mc->tables[mc->next_table]; + OTPChecker_Table_Generate(mc, table, &mc->calc, mc->tw_key, mc->tw_iv); +} + +static void work_done_handler (OTPChecker *mc) +{ + ASSERT(mc->tw_have) + DebugObject_Access(&mc->d_obj); + + // free work + BThreadWork_Free(&mc->tw); + mc->tw_have = 0; + + // update next table number + mc->next_table = bmodadd_int(mc->next_table, 1, mc->num_tables); + + // update number of used tables if not all are used yet + if (mc->tables_used < mc->num_tables) { + mc->tables_used++; + } + + // call handler + if (mc->handler) { + mc->handler(mc->user); + return; + } +} + +int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd) +{ + ASSERT(num_otps > 0) + ASSERT(BEncryption_cipher_valid(cipher)) + ASSERT(num_tables > 0) + + // init arguments + mc->num_otps = num_otps; + mc->cipher = cipher; + mc->num_tables = num_tables; + mc->twd = twd; + + // set no handlers + mc->handler = NULL; + + // set number of entries + if (mc->num_otps > INT_MAX / 2) { + goto fail0; + } + mc->num_entries = 2 * mc->num_otps; + + // set no tables used + mc->tables_used = 0; + mc->next_table = 0; + + // initialize calculator + if (!OTPCalculator_Init(&mc->calc, mc->num_otps, cipher)) { + goto fail0; + } + + // allocate tables + if (!(mc->tables = (struct OTPChecker_table *)BAllocArray(mc->num_tables, sizeof(mc->tables[0])))) { + goto fail1; + } + + // allocate entries + if (!(mc->entries = (struct OTPChecker_entry *)BAllocArray2(mc->num_tables, mc->num_entries, sizeof(mc->entries[0])))) { + goto fail2; + } + + // initialize tables + for (int i = 0; i < mc->num_tables; i++) { + struct OTPChecker_table *table = &mc->tables[i]; + table->entries = mc->entries + (size_t)i * mc->num_entries; + OTPChecker_Table_Empty(mc, table); + } + + // have no work + mc->tw_have = 0; + + DebugObject_Init(&mc->d_obj); + return 1; + +fail2: + BFree(mc->tables); +fail1: + OTPCalculator_Free(&mc->calc); +fail0: + return 0; +} + +void OTPChecker_Free (OTPChecker *mc) +{ + DebugObject_Free(&mc->d_obj); + + // free work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + } + + // free entries + BFree(mc->entries); + + // free tables + BFree(mc->tables); + + // free calculator + OTPCalculator_Free(&mc->calc); +} + +void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t *iv) +{ + ASSERT(mc->next_table >= 0) + ASSERT(mc->next_table < mc->num_tables) + DebugObject_Access(&mc->d_obj); + + // free existing work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + } + + // set table's seed ID + mc->tables[mc->next_table].id = seed_id; + + // copy key and IV + memcpy(mc->tw_key, key, BEncryption_cipher_key_size(mc->cipher)); + memcpy(mc->tw_iv, iv, BEncryption_cipher_block_size(mc->cipher)); + + // start work + BThreadWork_Init(&mc->tw, mc->twd, (BThreadWork_handler_done)work_done_handler, mc, (BThreadWork_work_func)work_func, mc); + + // set have work + mc->tw_have = 1; +} + +void OTPChecker_RemoveSeeds (OTPChecker *mc) +{ + DebugObject_Access(&mc->d_obj); + + // free existing work + if (mc->tw_have) { + BThreadWork_Free(&mc->tw); + mc->tw_have = 0; + } + + mc->tables_used = 0; + mc->next_table = 0; +} + +int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp) +{ + DebugObject_Access(&mc->d_obj); + + // try tables in reverse order + for (int i = 1; i <= mc->tables_used; i++) { + int table_index = bmodadd_int(mc->next_table, mc->num_tables - i, mc->num_tables); + if (table_index == mc->next_table && mc->tw_have) { + // ignore table that is being generated + continue; + } + + struct OTPChecker_table *table = &mc->tables[table_index]; + if (table->id == seed_id) { + return OTPChecker_Table_CheckOTP(mc, table, otp); + } + } + + return 0; +} + +void OTPChecker_SetHandlers (OTPChecker *mc, OTPChecker_handler handler, void *user) +{ + DebugObject_Access(&mc->d_obj); + + mc->handler = handler; + mc->user = user; +} diff --git a/external/badvpn_dns/security/OTPChecker.h b/external/badvpn_dns/security/OTPChecker.h new file mode 100644 index 00000000..d81a91c8 --- /dev/null +++ b/external/badvpn_dns/security/OTPChecker.h @@ -0,0 +1,148 @@ +/** + * @file OTPChecker.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object that checks OTPs agains known seeds. + */ + +#ifndef BADVPN_SECURITY_OTPCHECKER_H +#define BADVPN_SECURITY_OTPCHECKER_H + +#include + +#include +#include +#include +#include +#include +#include + +struct OTPChecker_entry { + otp_t otp; + int avail; +}; + +struct OTPChecker_table { + uint16_t id; + struct OTPChecker_entry *entries; +}; + +/** + * Handler called when OTP generation for a seed is finished and new OTPs + * can be recognized. + * + * @param user as in {@link OTPChecker_Init} + */ +typedef void (*OTPChecker_handler) (void *user); + +/** + * Object that checks OTPs agains known seeds. + */ +typedef struct { + BThreadWorkDispatcher *twd; + OTPChecker_handler handler; + void *user; + int num_otps; + int cipher; + int num_entries; + int num_tables; + int tables_used; + int next_table; + OTPCalculator calc; + struct OTPChecker_table *tables; + struct OTPChecker_entry *entries; + int tw_have; + BThreadWork tw; + uint8_t tw_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t tw_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + DebugObject d_obj; +} OTPChecker; + +/** + * Initializes the checker. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param mc the object + * @param num_otps number of OTPs to generate from a seed. Must be >0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @param num_tables number of tables to keep, each for one seed. Must be >0. + * @param twd thread work dispatcher + * @return 1 on success, 0 on failure + */ +int OTPChecker_Init (OTPChecker *mc, int num_otps, int cipher, int num_tables, BThreadWorkDispatcher *twd) WARN_UNUSED; + +/** + * Frees the checker. + * + * @param mc the object + */ +void OTPChecker_Free (OTPChecker *mc); + +/** + * Starts generating OTPs to recognize for a seed. + * OTPs for this seed will not be recognized until the {@link OTPChecker_handler} handler is called. + * If OTPs are still being generated for a previous seed, it will be forgotten. + * + * @param mc the object + * @param seed_id seed identifier + * @param key encryption key + * @param iv initialization vector + */ +void OTPChecker_AddSeed (OTPChecker *mc, uint16_t seed_id, uint8_t *key, uint8_t *iv); + +/** + * Removes all active seeds. + * + * @param mc the object + */ +void OTPChecker_RemoveSeeds (OTPChecker *mc); + +/** + * Checks an OTP. + * + * @param mc the object + * @param seed_id identifer of seed whom the OTP is claimed to belong to + * @param otp OTP to check + * @return 1 if the OTP is valid, 0 if not + */ +int OTPChecker_CheckOTP (OTPChecker *mc, uint16_t seed_id, otp_t otp); + +/** + * Sets handlers. + * + * @param mc the object + * @param handler handler to call when generation of new OTPs is complete, + * after {@link OTPChecker_AddSeed} was called. + * @param user argument to handler + */ +void OTPChecker_SetHandlers (OTPChecker *mc, OTPChecker_handler handler, void *user); + +#endif diff --git a/external/badvpn_dns/security/OTPGenerator.c b/external/badvpn_dns/security/OTPGenerator.c new file mode 100644 index 00000000..58a59f56 --- /dev/null +++ b/external/badvpn_dns/security/OTPGenerator.c @@ -0,0 +1,159 @@ +/** + * @file OTPGenerator.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +static void work_func (OTPGenerator *g) +{ + g->otps[!g->cur_calc] = OTPCalculator_Generate(&g->calc[!g->cur_calc], g->tw_key, g->tw_iv, 1); +} + +static void work_done_handler (OTPGenerator *g) +{ + ASSERT(g->tw_have) + DebugObject_Access(&g->d_obj); + + // free work + BThreadWork_Free(&g->tw); + g->tw_have = 0; + + // use new OTPs + g->cur_calc = !g->cur_calc; + g->position = 0; + + // call handler + g->handler(g->user); + return; +} + +int OTPGenerator_Init (OTPGenerator *g, int num_otps, int cipher, BThreadWorkDispatcher *twd, OTPGenerator_handler handler, void *user) +{ + ASSERT(num_otps >= 0) + ASSERT(BEncryption_cipher_valid(cipher)) + + // init arguments + g->num_otps = num_otps; + g->cipher = cipher; + g->twd = twd; + g->handler = handler; + g->user = user; + + // init position + g->position = g->num_otps; + + // init calculator + if (!OTPCalculator_Init(&g->calc[0], g->num_otps, g->cipher)) { + goto fail0; + } + + // init calculator + if (!OTPCalculator_Init(&g->calc[1], g->num_otps, g->cipher)) { + goto fail1; + } + + // set current calculator + g->cur_calc = 0; + + // have no work + g->tw_have = 0; + + DebugObject_Init(&g->d_obj); + return 1; + +fail1: + OTPCalculator_Free(&g->calc[0]); +fail0: + return 0; +} + +void OTPGenerator_Free (OTPGenerator *g) +{ + DebugObject_Free(&g->d_obj); + + // free work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + } + + // free calculator + OTPCalculator_Free(&g->calc[1]); + + // free calculator + OTPCalculator_Free(&g->calc[0]); +} + +void OTPGenerator_SetSeed (OTPGenerator *g, uint8_t *key, uint8_t *iv) +{ + DebugObject_Access(&g->d_obj); + + // free existing work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + } + + // copy key and IV + memcpy(g->tw_key, key, BEncryption_cipher_key_size(g->cipher)); + memcpy(g->tw_iv, iv, BEncryption_cipher_block_size(g->cipher)); + + // start work + BThreadWork_Init(&g->tw, g->twd, (BThreadWork_handler_done)work_done_handler, g, (BThreadWork_work_func)work_func, g); + + // set have work + g->tw_have = 1; +} + +int OTPGenerator_GetPosition (OTPGenerator *g) +{ + DebugObject_Access(&g->d_obj); + + return g->position; +} + +void OTPGenerator_Reset (OTPGenerator *g) +{ + DebugObject_Access(&g->d_obj); + + // free existing work + if (g->tw_have) { + BThreadWork_Free(&g->tw); + g->tw_have = 0; + } + + g->position = g->num_otps; +} + +otp_t OTPGenerator_GetOTP (OTPGenerator *g) +{ + ASSERT(g->position < g->num_otps) + DebugObject_Access(&g->d_obj); + + return g->otps[g->cur_calc][g->position++]; +} diff --git a/external/badvpn_dns/security/OTPGenerator.h b/external/badvpn_dns/security/OTPGenerator.h new file mode 100644 index 00000000..f2c83ddc --- /dev/null +++ b/external/badvpn_dns/security/OTPGenerator.h @@ -0,0 +1,134 @@ +/** + * @file OTPGenerator.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object which generates OTPs for use in sending packets. + */ + +#ifndef BADVPN_SECURITY_OTPGENERATOR_H +#define BADVPN_SECURITY_OTPGENERATOR_H + +#include +#include +#include +#include + +/** + * Handler called when OTP generation for a seed is finished. + * The OTP position is reset to zero before the handler is called. + * + * @param user as in {@link OTPGenerator_Init} + */ +typedef void (*OTPGenerator_handler) (void *user); + +/** + * Object which generates OTPs for use in sending packets. + */ +typedef struct { + int num_otps; + int cipher; + BThreadWorkDispatcher *twd; + OTPGenerator_handler handler; + void *user; + int position; + int cur_calc; + OTPCalculator calc[2]; + otp_t *otps[2]; + int tw_have; + BThreadWork tw; + uint8_t tw_key[BENCRYPTION_MAX_KEY_SIZE]; + uint8_t tw_iv[BENCRYPTION_MAX_BLOCK_SIZE]; + DebugObject d_obj; +} OTPGenerator; + +/** + * Initializes the generator. + * The object is initialized with number of used OTPs = num_otps. + * {@link BSecurity_GlobalInitThreadSafe} must have been done if + * {@link BThreadWorkDispatcher_UsingThreads}(twd) = 1. + * + * @param g the object + * @param num_otps number of OTPs to generate from a seed. Must be >=0. + * @param cipher encryption cipher for calculating the OTPs. Must be valid + * according to {@link BEncryption_cipher_valid}. + * @param twd thread work dispatcher + * @param handler handler to call when generation of new OTPs is complete, + * after {@link OTPGenerator_SetSeed} was called. + * @param user argument to handler + * @return 1 on success, 0 on failure + */ +int OTPGenerator_Init (OTPGenerator *g, int num_otps, int cipher, BThreadWorkDispatcher *twd, OTPGenerator_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the generator. + * + * @param g the object + */ +void OTPGenerator_Free (OTPGenerator *g); + +/** + * Starts generating OTPs for a seed. + * When generation is complete and the new OTPs may be used, the {@link OTPGenerator_handler} + * handler will be called. + * If OTPs are still being generated for a previous seed, it will be forgotten. + * This call by itself does not affect the OTP position; rather the position is set to zero + * before the handler is called. + * + * @param g the object + * @param key encryption key + * @param iv initialization vector + */ +void OTPGenerator_SetSeed (OTPGenerator *g, uint8_t *key, uint8_t *iv); + +/** + * Returns the number of OTPs used up from the current seed so far. + * If there is no seed yet, returns num_otps. + * + * @param g the object + * @return number of used OTPs + */ +int OTPGenerator_GetPosition (OTPGenerator *g); + +/** + * Sets the number of used OTPs to num_otps. + * + * @param g the object + */ +void OTPGenerator_Reset (OTPGenerator *g); + +/** + * Generates a single OTP. + * The number of used OTPs must be < num_otps. + * The number of used OTPs is incremented. + * + * @param g the object + */ +otp_t OTPGenerator_GetOTP (OTPGenerator *g); + +#endif diff --git a/external/badvpn_dns/server/CMakeLists.txt b/external/badvpn_dns/server/CMakeLists.txt new file mode 100644 index 00000000..1d02432d --- /dev/null +++ b/external/badvpn_dns/server/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(badvpn-server server.c) +target_link_libraries(badvpn-server system flow flowextra nspr_support predicate security ${NSPR_LIBRARIES} ${NSS_LIBRARIES}) + +install( + TARGETS badvpn-server + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-server.8 + DESTINATION share/man/man8 +) diff --git a/external/badvpn_dns/server/badvpn-server.8 b/external/badvpn_dns/server/badvpn-server.8 new file mode 100644 index 00000000..b8c60e5b --- /dev/null +++ b/external/badvpn_dns/server/badvpn-server.8 @@ -0,0 +1,190 @@ +.TH badvpn-server 8 "21 June 2011" +.SH NAME +badvpn-server \- chat server for the BadVPN peer-to-peer VPN system +.SH SYNOPSIS +.B badvpn-server +.RS +.RB "[" --help "]" +.br +.RB "[" --version "]" +.br +.RB "[" --logger " ]" +.br +(logger=syslog? +.br +.RS +.br +.RB "[" --syslog-facility " ]" +.br +.RB "[" --syslog-ident " ]" +.br +.RE +) +.br +.RB "[" --loglevel " <0-5/none/error/warning/notice/info/debug>]" +.br +.RB "[" --channel-loglevel " <0-5/none/error/warning/notice/info/debug>] ..." +.br +.RB "[" --listen-addr " ] ..." +.br +.RB "[" --ssl " " --nssdb " " --server-cert-name " ]" +.br +.RB "[" --comm-predicate " ]" +.br +.RB "[" --relay-predicate " ]" +.br +.RB "[" --client-socket-sndbuf " ]" +.br +.RE +.SH INTRODUCTION +.P +This page documents the BadVPN server, which is used in a BadVPN VPN network by peers to +talk to each other in order to establish data connections. For a general description of +BadVPN, see +.BR badvpn (7). +.SH DESCRIPTION +.P +The BadVPN server is a chat server used by nodes in the VPN network to talk to each other +in order to establish data connections. Once it initializes, the server only terminates +if a signal is received. +.SH OPTIONS +.P +The BadVPN server is configured entirely from command line. +.TP +.BR --help +Print version and command line syntax and exit. +.TP +.BR --version +Print version and exit. +.TP +.BR --logger " " +Select where to log messages. Default is stdout. Syslog is not available on Windows. +.TP +.BR --syslog-facility " " +When logging to syslog, set the logging facility. The facility name must be in lower case. +.TP +.BR --syslog-ident " " +When logging to syslog, set the ident. +.TP +.BR --loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the default logging level. +.TP +.BR --channel-loglevel " <0-5/none/error/warning/notice/info/debug>" +Set the logging level for a specific logging channel. +.TP +.BR --listen-addr " " +Add an address for the server to listen on. See below for address format. +.TP +.BR --ssl +Use TLS. Requires --nssdb and --server-cert-name. +.TP +.BR --nssdb " " +When using TLS, the NSS database to use. Probably something like sql:/some/folder. +.TP +.BR --server-cert-name " " +When using TLS, the name of the certificate to use. The certificate must be readily accessible. +.TP +.BR --comm-predicate " " +Set a predicate to define which pairs of clients are allowed to communicate. The predicate is a +logical expression; see below for details. Available functions: +.br +.BR p1name "(string)" +- true if the TLS common name of peer 1 equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR p1addr "(string)" +- true if the IP address of peer 1 equals the given string. The string must not be a name. +.br +.BR p2name "(string)" +- true if the TLS common name of peer 2 equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR p2addr "(string)" +- true if the IP address of peer 2 equals the given string. The string must not be a name. +.br +There is no rule as to which is peer 1 and which peer 2. When the server needs to determine +whether to allow two peers to communicate, it evaluates the predicate once and in no specific order. +.TP +.BR --relay-predicate " " +Set a predicate to define how peers can relay data through other peers. The predicate is a +logical expression; see below for details. If the predicate evaluates to true, peer P can relay data +through peer R. Available functions: +.br +.BR pname "(string)" +- true if the TLS common name of peer P peer equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR paddr "(string)" +- true if the IP address of peer P equals the given string. The string must not be a name. +.br +.BR rname "(string)" +- true if the TLS common name of peer R peer equals the given string. If TLS is not used, the common +name is assumed to be an empty string. +.br +.BR raddr "(string)" +- true if the IP address of peer R equals the given string. The string must not be a name. +.br +.TP +.BR --client-socket-sndbuf " " +Sets the value of the SO_SNDBUF socket option for client TCP sockets (zero to not set). Lower values +will improve fairness when data from multiple peers is being sent to a given peer, but may result in lower +bandwidth if the network's bandwidth-delay product to too big. +.SH "EXIT CODE" +.P +If initialization fails, exits with code 1. Otherwise runs until termination is requested and exits with code 1. +.SH "ADDRESS FORMAT" +.P +Addresses have the form ipaddr:port, where ipaddr is either an IPv4 address (name or numeric), or an +IPv6 address enclosed in brackets [] (name or numeric again). +.SH PREDICATES +.P +The BadVPN server includes a small predicate language used to define certain policies. +Syntax and semantics of the language are described here. +.TP +.BR true +Logical true constant. Evaluates to 1. +.TP +.BR false +Logical false constant. Evaluates to 0. +.TP +.BR NOT " expression" +Logical negation. If the expression evaluates to error, the +negation evaluates to error. +.TP +.RB "expression " OR " expression" +Logical disjunction. The second expression is only evaluated +if the first expression evaluates to false. If a sub-expression +evaluates to error, the disjunction evaluates to error. +.TP +.RB "expression " AND " expression" +Logical conjunction. The second expression is only evaluated +if the first expression evaluates to true. If a sub-expression +evaluates to error, the conjunction evaluates to error. +.TP +.RB function "(" "arg" "," " ..." "," " arg" ")" +Evaluation of a user-provided function (function is the name of the +function, [a-zA-Z0-9_]+). +If the function with the given name does not exist, it evaluates to +error. +Arguments are evaluated from left to right. Each argument can either +be a logical expression or a string (characters enclosed in double +quotes, without any double quote). +If an argument is encountered, but all needed arguments have already +been evaluated, the function evaluates to error. +If an argument is of wrong type, it is not evaluated and the function +evaluates to error. +If an argument evaluates to error, the function evaluates to error. +If after all arguments have been evaluated, the function needs more +arguments, it evaluates to error. +Then the handler function is called. If it returns anything other +than 1 and 0, the function evaluates to error. Otherwise it evaluates +to what the handler function returned. +.SH "EXAMPLES" +.P +For examples of using BadVPN, see +.BR badvpn (7). +.SH "SEE ALSO" +.BR badvpn-client (8), +.BR badvpn (7) +.SH AUTHORS +Ambroz Bizjak diff --git a/external/badvpn_dns/server/server.c b/external/badvpn_dns/server/server.c new file mode 100644 index 00000000..2b22101b --- /dev/null +++ b/external/badvpn_dns/server/server.c @@ -0,0 +1,2394 @@ +/** + * @file server.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +// NSPR and NSS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// BadVPN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +// parsed command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + int threads; + int use_threads_for_ssl_handshake; + int use_threads_for_ssl_data; + int ssl; + char *nssdb; + char *server_cert_name; + char *listen_addrs[MAX_LISTEN_ADDRS]; + int num_listen_addrs; + char *comm_predicate; + char *relay_predicate; + int client_socket_sndbuf; + int max_clients; +} options; + +// listen addresses +BAddr listen_addrs[MAX_LISTEN_ADDRS]; +int num_listen_addrs; + +// communication predicate +BPredicate comm_predicate; + +// communication predicate functions +BPredicateFunction comm_predicate_func_p1name; +BPredicateFunction comm_predicate_func_p2name; +BPredicateFunction comm_predicate_func_p1addr; +BPredicateFunction comm_predicate_func_p2addr; + +// variables when evaluating the predicate, adjusted before every evaluation +const char *comm_predicate_p1name; +const char *comm_predicate_p2name; +BIPAddr comm_predicate_p1addr; +BIPAddr comm_predicate_p2addr; + +// relay predicate +BPredicate relay_predicate; + +// gateway predicate functions +BPredicateFunction relay_predicate_func_pname; +BPredicateFunction relay_predicate_func_rname; +BPredicateFunction relay_predicate_func_paddr; +BPredicateFunction relay_predicate_func_raddr; + +// variables when evaluating the comm_predicate, adjusted before every evaluation +const char *relay_predicate_pname; +const char *relay_predicate_rname; +BIPAddr relay_predicate_paddr; +BIPAddr relay_predicate_raddr; + +// i/o system +BReactor ss; + +// thread work dispatcher +BThreadWorkDispatcher twd; + +// server certificate if using SSL +CERTCertificate *server_cert; + +// server private key if using SSL +SECKEYPrivateKey *server_key; + +// model NSPR file descriptor to speed up client initialization +PRFileDesc model_dprfd; +PRFileDesc *model_prfd; + +// listeners +BListener listeners[MAX_LISTEN_ADDRS]; +int num_listeners; + +// number of connected clients +int clients_num; + +// ID assigned to last connected client +peerid_t clients_nextid; + +// clients list +LinkedList1 clients; + +// clients tree (by ID) +BAVL clients_tree; + +// prints help text to standard output +static void print_help (const char *name); + +// prints program name and version to standard output +static void print_version (void); + +// parses the command line +static int parse_arguments (int argc, char *argv[]); + +// processes certain command line options +static int process_arguments (void); + +static int ssl_flags (void); + +// handler for program termination request +static void signal_handler (void *unused); + +// listener handler, accepts new clients +static void listener_handler (BListener *listener); + +// frees resources used by a client +static void client_dealloc (struct client_data *client); + +static int client_compute_buffer_size (struct client_data *client); + +// initializes the I/O porition of the client +static int client_init_io (struct client_data *client); + +// deallocates the I/O portion of the client. Must have no outgoing flows. +static void client_dealloc_io (struct client_data *client); + +// removes a client +static void client_remove (struct client_data *client); + +// job to finish removal after clients are informed +static void client_dying_job (struct client_data *client); + +// appends client log prefix +static void client_logfunc (struct client_data *client); + +// passes a message to the logger, prepending about the client +static void client_log (struct client_data *client, int level, const char *fmt, ...); + +// client activity timer handler. Removes the client. +static void client_disconnect_timer_handler (struct client_data *client); + +// BConnection handler +static void client_connection_handler (struct client_data *client, int event); + +// BSSLConnection handler +static void client_sslcon_handler (struct client_data *client, int event); + +// decoder handler +static void client_decoder_handler_error (struct client_data *client); + +// provides a buffer for sending a control packet to the client +static int client_start_control_packet (struct client_data *client, void **data, int len); + +// submits a packet written after client_start_control_packet +static void client_end_control_packet (struct client_data *client, uint8_t id); + +// sends a newclient message to a client +static int client_send_newclient (struct client_data *client, struct client_data *nc, int relay_server, int relay_client); + +// sends an endclient message to a client +static int client_send_endclient (struct client_data *client, peerid_t end_id); + +// handler for packets received from the client +static void client_input_handler_send (struct client_data *client, uint8_t *data, int data_len); + +// processes hello packets from clients +static void process_packet_hello (struct client_data *client, uint8_t *data, int data_len); + +// processes outmsg packets from clients +static void process_packet_outmsg (struct client_data *client, uint8_t *data, int data_len); + +// processes resetpeer packets from clients +static void process_packet_resetpeer (struct client_data *client, uint8_t *data, int data_len); + +// processes acceptpeer packets from clients +static void process_packet_acceptpeer (struct client_data *client, uint8_t *data, int data_len); + +// creates a peer flow +static struct peer_flow * peer_flow_create (struct client_data *src_client, struct client_data *dest_client); + +// deallocates a peer flow +static void peer_flow_dealloc (struct peer_flow *flow); + +static int peer_flow_init_io (struct peer_flow *flow); +static void peer_flow_free_io (struct peer_flow *flow); + +// disconnects the source client from a peer flow +static void peer_flow_disconnect (struct peer_flow *flow); + +// provides a buffer for sending a peer-to-peer packet +static int peer_flow_start_packet (struct peer_flow *flow, void **data, int len); + +// submits a peer-to-peer packet written after peer_flow_start_packet +static void peer_flow_end_packet (struct peer_flow *flow, uint8_t type); + +// handler called by the queue when a peer flow can be freed after its source has gone away +static void peer_flow_handler_canremove (struct peer_flow *flow); + +static void peer_flow_start_reset (struct peer_flow *flow); +static void peer_flow_drive_reset (struct peer_flow *flow); + +static void peer_flow_reset_qflow_handler_busy (struct peer_flow *flow); + +// resets clients knowledge after the timer expires +static void peer_flow_reset_timer_handler (struct peer_flow *flow); + +// generates a client ID to be used for a newly connected client +static peerid_t new_client_id (void); + +// finds a client by its ID +static struct client_data * find_client_by_id (peerid_t id); + +// checks if two clients are allowed to communicate. May depend on the order +// of the clients. +static int clients_allowed (struct client_data *client1, struct client_data *client2); + +// communication predicate function p1name +static int comm_predicate_func_p1name_cb (void *user, void **args); + +// communication predicate function p2name +static int comm_predicate_func_p2name_cb (void *user, void **args); + +// communication predicate function p1addr +static int comm_predicate_func_p1addr_cb (void *user, void **args); + +// communication predicate function p2addr +static int comm_predicate_func_p2addr_cb (void *user, void **args); + +// checks if relay is allowed for a client through another client +static int relay_allowed (struct client_data *client, struct client_data *relay); + +// relay predicate function pname +static int relay_predicate_func_pname_cb (void *user, void **args); + +// relay predicate function rname +static int relay_predicate_func_rname_cb (void *user, void **args); + +// relay predicate function paddr +static int relay_predicate_func_paddr_cb (void *user, void **args); + +// relay predicate function raddr +static int relay_predicate_func_raddr_cb (void *user, void **args); + +// comparator for peerid_t used in AVL tree +static int peerid_comparator (void *unused, peerid_t *p1, peerid_t *p2); + +static struct peer_know * create_know (struct client_data *from, struct client_data *to, int relay_server, int relay_client); +static void remove_know (struct peer_know *k); +static void know_inform_job_handler (struct peer_know *k); +static void uninform_know (struct peer_know *k); +static void know_uninform_job_handler (struct peer_know *k); + +static int launch_pair (struct peer_flow *flow_to); + +// find flow from a client to some client +static struct peer_flow * find_flow (struct client_data *client, peerid_t dest_id); + +int main (int argc, char *argv[]) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + if (options.ssl) { + // initialize NSPR + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + // initialize i/o layer types + if (!DummyPRFileDesc_GlobalInit()) { + BLog(BLOG_ERROR, "DummyPRFileDesc_GlobalInit failed"); + goto fail01; + } + if (!BSSLConnection_GlobalInit()) { + BLog(BLOG_ERROR, "BSSLConnection_GlobalInit failed"); + goto fail01; + } + + // initialize NSS + if (NSS_Init(options.nssdb) != SECSuccess) { + BLog(BLOG_ERROR, "NSS_Init failed (%d)", (int)PR_GetError()); + goto fail01; + } + if (NSS_SetDomesticPolicy() != SECSuccess) { + BLog(BLOG_ERROR, "NSS_SetDomesticPolicy failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // initialize server cache + if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigServerSessionIDCache failed (%d)", (int)PR_GetError()); + goto fail02; + } + + // open server certificate and private key + if (!open_nss_cert_and_key(options.server_cert_name, &server_cert, &server_key)) { + BLog(BLOG_ERROR, "Cannot open certificate and key"); + goto fail03; + } + + // initialize model SSL fd + DummyPRFileDesc_Create(&model_dprfd); + if (!(model_prfd = SSL_ImportFD(NULL, &model_dprfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&model_dprfd) == PR_SUCCESS) + goto fail04; + } + + // set server certificate + if (SSL_ConfigSecureServer(model_prfd, server_cert, server_key, NSS_FindCertKEAType(server_cert)) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ConfigSecureServer failed"); + goto fail05; + } + } + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init communication predicate + if (options.comm_predicate) { + // init predicate + if (!BPredicate_Init(&comm_predicate, options.comm_predicate)) { + BLog(BLOG_ERROR, "BPredicate_Init failed"); + goto fail1; + } + + // init functions + int args[] = {PREDICATE_TYPE_STRING}; + BPredicateFunction_Init(&comm_predicate_func_p1name, &comm_predicate, "p1name", args, 1, comm_predicate_func_p1name_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p2name, &comm_predicate, "p2name", args, 1, comm_predicate_func_p2name_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p1addr, &comm_predicate, "p1addr", args, 1, comm_predicate_func_p1addr_cb, NULL); + BPredicateFunction_Init(&comm_predicate_func_p2addr, &comm_predicate, "p2addr", args, 1, comm_predicate_func_p2addr_cb, NULL); + } + + // init relay predicate + if (options.relay_predicate) { + // init predicate + if (!BPredicate_Init(&relay_predicate, options.relay_predicate)) { + BLog(BLOG_ERROR, "BPredicate_Init failed"); + goto fail2; + } + + // init functions + int args[] = {PREDICATE_TYPE_STRING}; + BPredicateFunction_Init(&relay_predicate_func_pname, &relay_predicate, "pname", args, 1, relay_predicate_func_pname_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_rname, &relay_predicate, "rname", args, 1, relay_predicate_func_rname_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_paddr, &relay_predicate, "paddr", args, 1, relay_predicate_func_paddr_cb, NULL); + BPredicateFunction_Init(&relay_predicate_func_raddr, &relay_predicate, "raddr", args, 1, relay_predicate_func_raddr_cb, NULL); + } + + // init time + BTime_Init(); + + // initialize reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail3; + } + + // init thread work dispatcher + if (!BThreadWorkDispatcher_Init(&twd, &ss, options.threads)) { + BLog(BLOG_ERROR, "BThreadWorkDispatcher_Init failed"); + goto fail3a; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail4; + } + + // initialize number of clients + clients_num = 0; + + // first client ID will be zero + clients_nextid = 0; + + // initialize clients linked list + LinkedList1_Init(&clients); + + // initialize clients tree + BAVL_Init(&clients_tree, OFFSET_DIFF(struct client_data, id, tree_node), (BAVL_comparator)peerid_comparator, NULL); + + // initialize listeners + num_listeners = 0; + while (num_listeners < num_listen_addrs) { + if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "BListener_Init failed"); + goto fail10; + } + num_listeners++; + } + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&clients)) { + struct client_data *client = UPPER_OBJECT(node, struct client_data, list_node); + + // remove outgoing knows + LinkedList1Node *node2; + while (node2 = LinkedList1_GetFirst(&client->know_out_list)) { + struct peer_know *k = UPPER_OBJECT(node2, struct peer_know, from_node); + remove_know(k); + } + + // remove incoming knows + LinkedList1Node *node3; + while (node3 = LinkedList1_GetFirst(&client->know_in_list)) { + struct peer_know *k = UPPER_OBJECT(node3, struct peer_know, to_node); + remove_know(k); + } + + // remove outgoing flows + LinkedList1Node *flow_node; + while (flow_node = LinkedList1_GetFirst(&client->peer_out_flows_list)) { + struct peer_flow *flow = UPPER_OBJECT(flow_node, struct peer_flow, src_list_node); + ASSERT(flow->src_client == client) + + // allow freeing queue flows at dest + PacketPassFairQueue_PrepareFree(&flow->dest_client->output_peers_fairqueue); + + // deallocate flow + peer_flow_dealloc(flow); + } + + // deallocate client + client_dealloc(client); + } +fail10: + while (num_listeners > 0) { + num_listeners--; + BListener_Free(&listeners[num_listeners]); + } + + BSignal_Finish(); +fail4: + BThreadWorkDispatcher_Free(&twd); +fail3a: + BReactor_Free(&ss); +fail3: + if (options.relay_predicate) { + BPredicateFunction_Free(&relay_predicate_func_raddr); + BPredicateFunction_Free(&relay_predicate_func_paddr); + BPredicateFunction_Free(&relay_predicate_func_rname); + BPredicateFunction_Free(&relay_predicate_func_pname); + BPredicate_Free(&relay_predicate); + } +fail2: + if (options.comm_predicate) { + BPredicateFunction_Free(&comm_predicate_func_p2addr); + BPredicateFunction_Free(&comm_predicate_func_p1addr); + BPredicateFunction_Free(&comm_predicate_func_p2name); + BPredicateFunction_Free(&comm_predicate_func_p1name); + BPredicate_Free(&comm_predicate); + } +fail1: + if (options.ssl) { +fail05: + ASSERT_FORCE(PR_Close(model_prfd) == PR_SUCCESS) +fail04: + CERT_DestroyCertificate(server_cert); + SECKEY_DestroyPrivateKey(server_key); +fail03: + ASSERT_FORCE(SSL_ShutdownServerSessionIDCache() == SECSuccess) +fail02: + ASSERT_FORCE(NSS_Shutdown() == SECSuccess) +fail01: + ASSERT_FORCE(PR_Cleanup() == PR_SUCCESS) + PL_ArenaFinish(); + } + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--threads ]\n" + " [--use-threads-for-ssl-handshake]\n" + " [--use-threads-for-ssl-data]\n" + " [--listen-addr ] ...\n" + " [--ssl --nssdb --server-cert-name ]\n" + " [--comm-predicate ]\n" + " [--relay-predicate ]\n" + " [--client-socket-sndbuf ]\n" + " [--max-clients ]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.threads = 0; + options.use_threads_for_ssl_handshake = 0; + options.use_threads_for_ssl_data = 0; + options.ssl = 0; + options.nssdb = NULL; + options.server_cert_name = NULL; + options.num_listen_addrs = 0; + options.comm_predicate = NULL; + options.relay_predicate = NULL; + options.client_socket_sndbuf = CLIENT_DEFAULT_SOCKET_SNDBUF; + options.max_clients = DEFAULT_MAX_CLIENTS; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (i + 1 >= argc) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--threads")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.threads = atoi(argv[i + 1]); + i++; + } + else if (!strcmp(arg, "--use-threads-for-ssl-handshake")) { + options.use_threads_for_ssl_handshake = 1; + } + else if (!strcmp(arg, "--use-threads-for-ssl-data")) { + options.use_threads_for_ssl_data = 1; + } + else if (!strcmp(arg, "--ssl")) { + options.ssl = 1; + } + else if (!strcmp(arg, "--nssdb")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.nssdb = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--server-cert-name")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.server_cert_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_listen_addrs == MAX_LISTEN_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.listen_addrs[options.num_listen_addrs] = argv[i + 1]; + options.num_listen_addrs++; + i++; + } + else if (!strcmp(arg, "--comm-predicate")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.comm_predicate = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--relay-predicate")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.relay_predicate = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--client-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.client_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else { + fprintf(stderr, "%s: unknown option\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!!options.nssdb != options.ssl) { + fprintf(stderr, "--ssl and --nssdb must be used together\n"); + return 0; + } + + if (!!options.server_cert_name != options.ssl) { + fprintf(stderr, "--ssl and --server-cert-name must be used together\n"); + return 0; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen addresses + num_listen_addrs = 0; + while (num_listen_addrs < options.num_listen_addrs) { + if (!BAddr_Parse(&listen_addrs[num_listen_addrs], options.listen_addrs[num_listen_addrs], NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + num_listen_addrs++; + } + + return 1; +} + +int ssl_flags (void) +{ + int flags = 0; + if (options.use_threads_for_ssl_handshake) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE; + } + if (options.use_threads_for_ssl_data) { + flags |= BSSLCONNECTION_FLAG_THREADWORK_IO; + } + return flags; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 0); +} + +void listener_handler (BListener *listener) +{ + if (clients_num == options.max_clients) { + BLog(BLOG_WARNING, "too many clients for new client"); + goto fail0; + } + + // allocate the client structure + struct client_data *client = (struct client_data *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "failed to allocate client"); + goto fail0; + } + + // accept connection + if (!BConnection_Init(&client->con, BConnection_source_listener(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // limit socket send buffer, else our scheduling is pointless + if (options.client_socket_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&client->con, options.client_socket_sndbuf)) { + BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + // assign ID + client->id = new_client_id(); + + // set no common name + client->common_name = NULL; + + // now client_log() works + + // init connection interfaces + BConnection_SendAsync_Init(&client->con); + BConnection_RecvAsync_Init(&client->con); + + if (options.ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&client->bottom_prfd, BConnection_SendAsync_GetIf(&client->con), BConnection_RecvAsync_GetIf(&client->con), &twd, ssl_flags())) { + client_log(client, BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail2; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(client->ssl_prfd = SSL_ImportFD(model_prfd, &client->bottom_prfd))) { + client_log(client, BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&client->bottom_prfd) == PR_SUCCESS) + goto fail2; + } + + // set server mode + if (SSL_ResetHandshake(client->ssl_prfd, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail3; + } + + // set require client certificate + if (SSL_OptionSet(client->ssl_prfd, SSL_REQUEST_CERTIFICATE, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_OptionSet(SSL_REQUEST_CERTIFICATE) failed"); + goto fail3; + } + if (SSL_OptionSet(client->ssl_prfd, SSL_REQUIRE_CERTIFICATE, PR_TRUE) != SECSuccess) { + client_log(client, BLOG_ERROR, "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE) failed"); + goto fail3; + } + + // init SSL connection + BSSLConnection_Init(&client->sslcon, client->ssl_prfd, 1, BReactor_PendingGroup(&ss), client, (BSSLConnection_handler)client_sslcon_handler); + } else { + // initialize I/O + if (!client_init_io(client)) { + goto fail2; + } + } + + // start disconnect timer + BTimer_Init(&client->disconnect_timer, CLIENT_NO_DATA_TIME_LIMIT, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // link in + clients_num++; + LinkedList1_Append(&clients, &client->list_node); + ASSERT_EXECUTE(BAVL_Insert(&clients_tree, &client->tree_node, NULL)) + + // init knowledge lists + LinkedList1_Init(&client->know_out_list); + LinkedList1_Init(&client->know_in_list); + + // initialize peer flows from us list and tree (flows for sending messages to other clients) + LinkedList1_Init(&client->peer_out_flows_list); + BAVL_Init(&client->peer_out_flows_tree, OFFSET_DIFF(struct peer_flow, dest_client_id, src_tree_node), (BAVL_comparator)peerid_comparator, NULL); + + // init dying + client->dying = 0; + BPending_Init(&client->dying_job, BReactor_PendingGroup(&ss), (BPending_handler)client_dying_job, client); + + // set state + client->initstatus = (options.ssl ? INITSTATUS_HANDSHAKE : INITSTATUS_WAITHELLO); + + client_log(client, BLOG_INFO, "initialized"); + + return; + + if (options.ssl) { +fail3: + ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS) + } +fail2: + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_dealloc (struct client_data *client) +{ + ASSERT(LinkedList1_IsEmpty(&client->know_out_list)) + ASSERT(LinkedList1_IsEmpty(&client->know_in_list)) + ASSERT(LinkedList1_IsEmpty(&client->peer_out_flows_list)) + + // free I/O + if (client->initstatus >= INITSTATUS_WAITHELLO && !client->dying) { + client_dealloc_io(client); + } + + // free dying + BPending_Free(&client->dying_job); + + // link out + BAVL_Remove(&clients_tree, &client->tree_node); + LinkedList1_Remove(&clients, &client->list_node); + clients_num--; + + // stop disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free SSL + if (options.ssl) { + BSSLConnection_Free(&client->sslcon); + ASSERT_FORCE(PR_Close(client->ssl_prfd) == PR_SUCCESS) + } + + // free common name + if (client->common_name) { + PORT_Free(client->common_name); + } + + // free connection interfaces + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free memory + free(client); +} + +int client_compute_buffer_size (struct client_data *client) +{ + bsize_t s = bsize_add(bsize_fromsize(1), bsize_mul(bsize_fromsize(2), bsize_fromsize(options.max_clients - 1))); + + if (s.is_overflow || s.value > INT_MAX) { + return INT_MAX; + } else { + return s.value; + } +} + +int client_init_io (struct client_data *client) +{ + StreamPassInterface *send_if = (options.ssl ? BSSLConnection_GetSendIf(&client->sslcon) : BConnection_SendAsync_GetIf(&client->con)); + StreamRecvInterface *recv_if = (options.ssl ? BSSLConnection_GetRecvIf(&client->sslcon) : BConnection_RecvAsync_GetIf(&client->con)); + + // init input + + // init interface + PacketPassInterface_Init(&client->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)client_input_handler_send, client, BReactor_PendingGroup(&ss)); + + // init decoder + if (!PacketProtoDecoder_Init(&client->input_decoder, recv_if, &client->input_interface, BReactor_PendingGroup(&ss), client, + (PacketProtoDecoder_handler_error)client_decoder_handler_error + )) { + client_log(client, BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init output common + + // init sender + PacketStreamSender_Init(&client->output_sender, send_if, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(&ss)); + + // init queue + PacketPassPriorityQueue_Init(&client->output_priorityqueue, PacketStreamSender_GetInput(&client->output_sender), BReactor_PendingGroup(&ss), 0); + + // init output control flow + + // init queue flow + PacketPassPriorityQueueFlow_Init(&client->output_control_qflow, &client->output_priorityqueue, -1); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init( + &client->output_control_oflow, SC_MAX_ENC, client_compute_buffer_size(client), + PacketPassPriorityQueueFlow_GetInput(&client->output_control_qflow), BReactor_PendingGroup(&ss) + )) { + client_log(client, BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail2; + } + client->output_control_input = PacketProtoFlow_GetInput(&client->output_control_oflow); + client->output_control_packet_len = -1; + + // init output peers flow + + // init queue flow + // use lower priority than control flow (higher number) + PacketPassPriorityQueueFlow_Init(&client->output_peers_qflow, &client->output_priorityqueue, 0); + + // init fair queue (for different peers) + if (!PacketPassFairQueue_Init(&client->output_peers_fairqueue, PacketPassPriorityQueueFlow_GetInput(&client->output_peers_qflow), BReactor_PendingGroup(&ss), 0, 1)) { + client_log(client, BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail3; + } + + // init list of flows + LinkedList1_Init(&client->output_peers_flows); + + return 1; + +fail3: + PacketPassPriorityQueueFlow_Free(&client->output_peers_qflow); + PacketProtoFlow_Free(&client->output_control_oflow); +fail2: + PacketPassPriorityQueueFlow_Free(&client->output_control_qflow); + // free output common + PacketPassPriorityQueue_Free(&client->output_priorityqueue); + PacketStreamSender_Free(&client->output_sender); + // free input + PacketProtoDecoder_Free(&client->input_decoder); +fail1: + PacketPassInterface_Free(&client->input_interface); + return 0; +} + +void client_dealloc_io (struct client_data *client) +{ + // stop using any buffers before they get freed + if (options.ssl) { + BSSLConnection_ReleaseBuffers(&client->sslcon); + } + + // allow freeing fair queue flows + PacketPassFairQueue_PrepareFree(&client->output_peers_fairqueue); + + // remove flows to us + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&client->output_peers_flows)) { + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, dest_list_node); + ASSERT(flow->dest_client == client) + peer_flow_dealloc(flow); + } + + // allow freeing priority queue flows + PacketPassPriorityQueue_PrepareFree(&client->output_priorityqueue); + + // free output peers flow + PacketPassFairQueue_Free(&client->output_peers_fairqueue); + PacketPassPriorityQueueFlow_Free(&client->output_peers_qflow); + + // free output control flow + PacketProtoFlow_Free(&client->output_control_oflow); + PacketPassPriorityQueueFlow_Free(&client->output_control_qflow); + + // free output common + PacketPassPriorityQueue_Free(&client->output_priorityqueue); + PacketStreamSender_Free(&client->output_sender); + + // free input + PacketProtoDecoder_Free(&client->input_decoder); + PacketPassInterface_Free(&client->input_interface); +} + +void client_remove (struct client_data *client) +{ + ASSERT(!client->dying) + + client_log(client, BLOG_INFO, "removing"); + + // set dying to prevent sending this client anything + client->dying = 1; + + // free I/O now, removing incoming flows + if (client->initstatus >= INITSTATUS_WAITHELLO) { + client_dealloc_io(client); + } + + // remove outgoing knows + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&client->know_out_list)) { + struct peer_know *k = UPPER_OBJECT(node, struct peer_know, from_node); + remove_know(k); + } + + // remove outgoing flows + while (node = LinkedList1_GetFirst(&client->peer_out_flows_list)) { + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, src_list_node); + ASSERT(flow->src_client == client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + + if (flow->have_io && PacketPassFairQueueFlow_IsBusy(&flow->qflow)) { + client_log(client, BLOG_DEBUG, "removing flow to %d later", (int)flow->dest_client->id); + peer_flow_disconnect(flow); + } else { + client_log(client, BLOG_DEBUG, "removing flow to %d now", (int)flow->dest_client->id); + peer_flow_dealloc(flow); + } + } + + // schedule job to finish removal after clients are informed + BPending_Set(&client->dying_job); + + // inform other clients that 'client' is no more + node = LinkedList1_GetFirst(&client->know_in_list); + while (node) { + LinkedList1Node *next = LinkedList1Node_Next(node); + struct peer_know *k = UPPER_OBJECT(node, struct peer_know, to_node); + uninform_know(k); + node = next; + } +} + +void client_dying_job (struct client_data *client) +{ + ASSERT(client->dying) + ASSERT(LinkedList1_IsEmpty(&client->know_in_list)) + + client_dealloc(client); + return; +} + +void client_logfunc (struct client_data *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client %d (%s)", (int)client->id, addr); + if (client->common_name) { + BLog_Append(" (%s)", client->common_name); + } + BLog_Append(": "); +} + +void client_log (struct client_data *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client_data *client) +{ + ASSERT(!client->dying) + + client_log(client, BLOG_INFO, "timed out"); + + client_remove(client); + return; +} + +void client_connection_handler (struct client_data *client, int event) +{ + ASSERT(!client->dying) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "connection closed"); + } else { + client_log(client, BLOG_INFO, "connection error"); + } + + client_remove(client); + return; +} + +void client_sslcon_handler (struct client_data *client, int event) +{ + ASSERT(options.ssl) + ASSERT(!client->dying) + ASSERT(event == BSSLCONNECTION_EVENT_UP || event == BSSLCONNECTION_EVENT_ERROR) + ASSERT(!(event == BSSLCONNECTION_EVENT_UP) || client->initstatus == INITSTATUS_HANDSHAKE) + + if (event == BSSLCONNECTION_EVENT_ERROR) { + client_log(client, BLOG_ERROR, "SSL error"); + client_remove(client); + return; + } + + // get client certificate + CERTCertificate *cert = SSL_PeerCertificate(client->ssl_prfd); + if (!cert) { + client_log(client, BLOG_ERROR, "SSL_PeerCertificate failed"); + goto fail0; + } + + // remember common name + if (!(client->common_name = CERT_GetCommonName(&cert->subject))) { + client_log(client, BLOG_NOTICE, "CERT_GetCommonName failed"); + goto fail1; + } + + // store certificate + SECItem der = cert->derCert; + if (der.len > sizeof(client->cert)) { + client_log(client, BLOG_NOTICE, "client certificate too big"); + goto fail1; + } + memcpy(client->cert, der.data, der.len); + client->cert_len = der.len; + + PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + client_log(client, BLOG_ERROR, "PORT_NewArena failed"); + goto fail1; + } + + // encode certificate + memset(&der, 0, sizeof(der)); + if (!SEC_ASN1EncodeItem(arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate))) { + client_log(client, BLOG_ERROR, "SEC_ASN1EncodeItem failed"); + goto fail2; + } + + // store re-encoded certificate (for compatibility with old clients) + if (der.len > sizeof(client->cert_old)) { + client_log(client, BLOG_NOTICE, "client certificate too big"); + goto fail2; + } + memcpy(client->cert_old, der.data, der.len); + client->cert_old_len = der.len; + + // init I/O chains + if (!client_init_io(client)) { + goto fail2; + } + + PORT_FreeArena(arena, PR_FALSE); + CERT_DestroyCertificate(cert); + + // set client state + client->initstatus = INITSTATUS_WAITHELLO; + + client_log(client, BLOG_INFO, "handshake complete"); + + return; + + // handle errors +fail2: + PORT_FreeArena(arena, PR_FALSE); +fail1: + CERT_DestroyCertificate(cert); +fail0: + client_remove(client); +} + +void client_decoder_handler_error (struct client_data *client) +{ + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + + client_log(client, BLOG_ERROR, "decoder error"); + + client_remove(client); + return; +} + +int client_start_control_packet (struct client_data *client, void **data, int len) +{ + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + ASSERT(client->output_control_packet_len == -1) + +#ifdef SIMULATE_OUT_OF_CONTROL_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_OUT_OF_CONTROL_BUFFER) { + client_log(client, BLOG_INFO, "out of control buffer, removing"); + client_remove(client); + return -1; + } +#endif + + // obtain location for writing the packet + if (!BufferWriter_StartPacket(client->output_control_input, &client->output_control_packet)) { + // out of buffer, kill client + client_log(client, BLOG_INFO, "out of control buffer, removing"); + client_remove(client); + return -1; + } + + client->output_control_packet_len = len; + + if (data) { + *data = client->output_control_packet + sizeof(struct sc_header); + } + + return 0; +} + +void client_end_control_packet (struct client_data *client, uint8_t type) +{ + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + ASSERT(client->output_control_packet_len >= 0) + ASSERT(client->output_control_packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = htol8(type); + memcpy(client->output_control_packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(client->output_control_input, sizeof(struct sc_header) + client->output_control_packet_len); + + client->output_control_packet_len = -1; +} + +int client_send_newclient (struct client_data *client, struct client_data *nc, int relay_server, int relay_client) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + ASSERT(nc->initstatus == INITSTATUS_COMPLETE) + ASSERT(!nc->dying) + + int flags = 0; + if (relay_server) { + flags |= SCID_NEWCLIENT_FLAG_RELAY_SERVER; + } + if (relay_client) { + flags |= SCID_NEWCLIENT_FLAG_RELAY_CLIENT; + } + if (options.ssl && client->version > SC_OLDVERSION_NOSSL && nc->version > SC_OLDVERSION_NOSSL) { + flags |= SCID_NEWCLIENT_FLAG_SSL; + } + + uint8_t *cert_data = NULL; + int cert_len = 0; + if (options.ssl) { + cert_data = (client->version == SC_OLDVERSION_BROKENCERT ? nc->cert_old : nc->cert); + cert_len = (client->version == SC_OLDVERSION_BROKENCERT ? nc->cert_old_len : nc->cert_len); + } + + struct sc_server_newclient omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg) + cert_len) < 0) { + return -1; + } + omsg.id = htol16(nc->id); + omsg.flags = htol16(flags); + memcpy(pack, &omsg, sizeof(omsg)); + if (cert_len > 0) { + memcpy((char *)pack + sizeof(omsg), cert_data, cert_len); + } + client_end_control_packet(client, SCID_NEWCLIENT); + + return 0; +} + +int client_send_endclient (struct client_data *client, peerid_t end_id) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + + struct sc_server_endclient omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg)) < 0) { + return -1; + } + omsg.id = htol16(end_id); + memcpy(pack, &omsg, sizeof(omsg)); + client_end_control_packet(client, SCID_ENDCLIENT); + + return 0; +} + +void client_input_handler_send (struct client_data *client, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_ENC) + ASSERT(INITSTATUS_HASLINK(client->initstatus)) + ASSERT(!client->dying) + + // accept packet + PacketPassInterface_Done(&client->input_interface); + + // restart disconnect timer + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // parse header + if (data_len < sizeof(struct sc_header)) { + client_log(client, BLOG_NOTICE, "packet too short"); + client_remove(client); + return; + } + struct sc_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t type = ltoh8(header.type); + + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_PAYLOAD) + + // perform action based on packet type + switch (type) { + case SCID_KEEPALIVE: + client_log(client, BLOG_DEBUG, "received keep-alive"); + return; + case SCID_CLIENTHELLO: + process_packet_hello(client, data, data_len); + return; + case SCID_OUTMSG: + process_packet_outmsg(client, data, data_len); + return; + case SCID_RESETPEER: + process_packet_resetpeer(client, data, data_len); + return; + case SCID_ACCEPTPEER: + process_packet_acceptpeer(client, data, data_len); + return; + default: + client_log(client, BLOG_NOTICE, "unknown packet type %d, removing", (int)type); + client_remove(client); + return; + } +} + +void process_packet_hello (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_WAITHELLO) { + client_log(client, BLOG_NOTICE, "hello: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_hello)) { + client_log(client, BLOG_NOTICE, "hello: invalid length"); + client_remove(client); + return; + } + + struct sc_client_hello msg; + memcpy(&msg, data, sizeof(msg)); + client->version = ltoh16(msg.version); + + switch (client->version) { + case SC_VERSION: + case SC_OLDVERSION_NOSSL: + case SC_OLDVERSION_BROKENCERT: + break; + default: + client_log(client, BLOG_ERROR, "hello: unknown version (%d)", client->version); + client_remove(client); + return; + } + + client_log(client, BLOG_INFO, "received hello"); + + // set client state to complete + client->initstatus = INITSTATUS_COMPLETE; + + // publish client + for (LinkedList1Node *list_node = LinkedList1_GetFirst(&clients); list_node; list_node = LinkedList1Node_Next(list_node)) { + struct client_data *client2 = UPPER_OBJECT(list_node, struct client_data, list_node); + if (client2 == client || client2->initstatus != INITSTATUS_COMPLETE || client2->dying || !clients_allowed(client, client2)) { + continue; + } + + // create flow from client to client2 + struct peer_flow *flow_to = peer_flow_create(client, client2); + if (!flow_to) { + client_log(client, BLOG_ERROR, "failed to allocate flow to %d", (int)client2->id); + goto fail; + } + + // create flow from client2 to client + struct peer_flow *flow_from = peer_flow_create(client2, client); + if (!flow_from) { + client_log(client, BLOG_ERROR, "failed to allocate flow from %d", (int)client2->id); + goto fail; + } + + // set opposite flow pointers + flow_to->opposite = flow_from; + flow_from->opposite = flow_to; + + // launch pair + if (!launch_pair(flow_to)) { + return; + } + } + + // send hello + struct sc_server_hello omsg; + void *pack; + if (client_start_control_packet(client, &pack, sizeof(omsg)) < 0) { + return; + } + omsg.flags = htol16(0); + omsg.id = htol16(client->id); + omsg.clientAddr = (client->addr.type == BADDR_TYPE_IPV4 ? client->addr.ipv4.ip : hton32(0)); + memcpy(pack, &omsg, sizeof(omsg)); + client_end_control_packet(client, SCID_SERVERHELLO); + + return; + +fail: + client_remove(client); +} + +void process_packet_outmsg (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "outmsg: not expected"); + client_remove(client); + return; + } + + if (data_len < sizeof(struct sc_client_outmsg)) { + client_log(client, BLOG_NOTICE, "outmsg: wrong size"); + client_remove(client); + return; + } + + struct sc_client_outmsg msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + int payload_size = data_len - sizeof(struct sc_client_outmsg); + + if (payload_size > SC_MAX_MSGLEN) { + client_log(client, BLOG_NOTICE, "outmsg: too large payload"); + client_remove(client); + return; + } + + uint8_t *payload = data + sizeof(struct sc_client_outmsg); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + client_log(client, BLOG_INFO, "no flow for message to %d", (int)id); + return; + } + + // if pair is resetting, ignore message + if (flow->resetting || flow->opposite->resetting) { + client_log(client, BLOG_INFO, "pair is resetting; not forwarding message to %d", (int)id); + return; + } + + // if sending client hasn't accepted yet, ignore message + if (!flow->accepted) { + client_log(client, BLOG_INFO, "client hasn't accepted; not forwarding message to %d", (int)id); + return; + } + +#ifdef SIMULATE_OUT_OF_FLOW_BUFFER + uint8_t x; + BRandom_randomize(&x, sizeof(x)); + if (x < SIMULATE_OUT_OF_FLOW_BUFFER) { + client_log(client, BLOG_WARNING, "simulating error; resetting to %d", (int)flow->dest_client->id); + peer_flow_start_reset(flow); + return; + } +#endif + + // send packet + struct sc_server_inmsg omsg; + void *pack; + if (!peer_flow_start_packet(flow, &pack, sizeof(omsg) + payload_size)) { + // out of buffer, reset these two clients + client_log(client, BLOG_WARNING, "out of buffer; resetting to %d", (int)flow->dest_client->id); + peer_flow_start_reset(flow); + return; + } + omsg.clientid = htol16(client->id); + memcpy(pack, &omsg, sizeof(omsg)); + memcpy((char *)pack + sizeof(omsg), payload, payload_size); + peer_flow_end_packet(flow, SCID_INMSG); +} + +void process_packet_resetpeer (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "resetpeer: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_resetpeer)) { + client_log(client, BLOG_NOTICE, "resetpeer: wrong size"); + client_remove(client); + return; + } + + struct sc_client_resetpeer msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + client_log(client, BLOG_INFO, "no flow for reset to %d", (int)id); + return; + } + + // if pair is resetting, ignore message + if (flow->resetting || flow->opposite->resetting) { + client_log(client, BLOG_INFO, "pair is resetting; not resetting to %d", (int)id); + return; + } + + // if sending client hasn't accepted yet, ignore message + if (!flow->accepted) { + client_log(client, BLOG_INFO, "client hasn't accepted; not resetting to %d", (int)id); + return; + } + + client_log(client, BLOG_WARNING, "resetting to %d", (int)flow->dest_client->id); + + // reset clients + peer_flow_start_reset(flow); +} + +void process_packet_acceptpeer (struct client_data *client, uint8_t *data, int data_len) +{ + if (client->initstatus != INITSTATUS_COMPLETE) { + client_log(client, BLOG_NOTICE, "acceptpeer: not expected"); + client_remove(client); + return; + } + + if (data_len != sizeof(struct sc_client_acceptpeer)) { + client_log(client, BLOG_NOTICE, "acceptpeer: wrong size"); + client_remove(client); + return; + } + + struct sc_client_acceptpeer msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.clientid); + + // lookup flow to destination client + struct peer_flow *flow = find_flow(client, id); + if (!flow) { + // the specified client has probably gone away but the sending client didn't know + // that yet; this is expected + client_log(client, BLOG_INFO, "acceptpeer: no flow to %d", (int)id); + return; + } + + // client can only accept once + if (flow->accepted) { + // the previous accept is probably from an old client with the same ID as this one; + // this is bad, disconnect client + client_log(client, BLOG_ERROR, "acceptpeer: already accepted to %d", (int)id); + client_remove(client); + return; + } + + client_log(client, BLOG_INFO, "accepted %d", (int)id); + + // set accepted + flow->accepted = 1; + + // if pair is resetting, continue + if (flow->resetting) { + peer_flow_drive_reset(flow); + } else if (flow->opposite->resetting) { + peer_flow_drive_reset(flow->opposite); + } +} + +struct peer_flow * peer_flow_create (struct client_data *src_client, struct client_data *dest_client) +{ + ASSERT(src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!src_client->dying) + ASSERT(dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!dest_client->dying) + ASSERT(!find_flow(src_client, dest_client->id)) + + // allocate flow structure + struct peer_flow *flow = (struct peer_flow *)malloc(sizeof(*flow)); + if (!flow) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set source and destination + flow->src_client = src_client; + flow->dest_client = dest_client; + flow->dest_client_id = dest_client->id; + + // add to source list and tree + LinkedList1_Append(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + ASSERT_EXECUTE(BAVL_Insert(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node, NULL)) + + // add to destination client list + LinkedList1_Append(&flow->dest_client->output_peers_flows, &flow->dest_list_node); + + // have no I/O + flow->have_io = 0; + + // init reset timer + BTimer_Init(&flow->reset_timer, CLIENT_RESET_TIME, (BTimer_handler)peer_flow_reset_timer_handler, flow); + + return flow; + +fail0: + return NULL; +} + +void peer_flow_dealloc (struct peer_flow *flow) +{ + if (flow->have_io) { PacketPassFairQueueFlow_AssertFree(&flow->qflow); } + + // free reset timer + BReactor_RemoveTimer(&ss, &flow->reset_timer); + + // free I/O + if (flow->have_io) { + peer_flow_free_io(flow); + } + + // remove from destination client list + LinkedList1_Remove(&flow->dest_client->output_peers_flows, &flow->dest_list_node); + + // remove from source list and hash table + if (flow->src_client) { + BAVL_Remove(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node); + LinkedList1_Remove(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + } + + // free memory + free(flow); +} + +int peer_flow_init_io (struct peer_flow *flow) +{ + ASSERT(!flow->have_io) + + // init queue flow + PacketPassFairQueueFlow_Init(&flow->qflow, &flow->dest_client->output_peers_fairqueue); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init( + &flow->oflow, SC_MAX_ENC, CLIENT_PEER_FLOW_BUFFER_MIN_PACKETS, + PacketPassFairQueueFlow_GetInput(&flow->qflow), BReactor_PendingGroup(&ss) + )) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + flow->input = PacketProtoFlow_GetInput(&flow->oflow); + + // set no packet + flow->packet_len = -1; + + // set have I/O + flow->have_io = 1; + + return 1; + +fail1: + PacketPassFairQueueFlow_Free(&flow->qflow); + return 0; +} + +void peer_flow_free_io (struct peer_flow *flow) +{ + ASSERT(flow->have_io) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + // free PacketProtoFlow + PacketProtoFlow_Free(&flow->oflow); + + // free queue flow + PacketPassFairQueueFlow_Free(&flow->qflow); + + // set have no I/O + flow->have_io = 0; +} + +void peer_flow_disconnect (struct peer_flow *flow) +{ + ASSERT(flow->src_client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->have_io) + ASSERT(PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + + // stop reset timer + BReactor_RemoveTimer(&ss, &flow->reset_timer); + + // remove from source list and hash table + BAVL_Remove(&flow->src_client->peer_out_flows_tree, &flow->src_tree_node); + LinkedList1_Remove(&flow->src_client->peer_out_flows_list, &flow->src_list_node); + + // set no source + flow->src_client = NULL; + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)peer_flow_handler_canremove, flow); +} + +int peer_flow_start_packet (struct peer_flow *flow, void **data, int len) +{ + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(!flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(flow->packet_len == -1) + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(!(len > 0) || data) + + // obtain location for writing the packet + if (!BufferWriter_StartPacket(flow->input, &flow->packet)) { + return 0; + } + + // remember packet length + flow->packet_len = len; + + if (data) { + *data = flow->packet + sizeof(struct sc_header); + } + return 1; +} + +void peer_flow_end_packet (struct peer_flow *flow, uint8_t type) +{ + ASSERT(flow->have_io) + ASSERT(flow->packet_len >= 0) + ASSERT(flow->packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = type; + memcpy(flow->packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(flow->input, sizeof(struct sc_header) + flow->packet_len); + + // set have no packet + flow->packet_len = -1; +} + +void peer_flow_handler_canremove (struct peer_flow *flow) +{ + ASSERT(!flow->src_client) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->have_io) + PacketPassFairQueueFlow_AssertFree(&flow->qflow); + + client_log(flow->dest_client, BLOG_DEBUG, "removing old flow"); + + peer_flow_dealloc(flow); + return; +} + +void peer_flow_start_reset (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(!flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(flow->opposite->have_io) + + client_log(flow->src_client, BLOG_INFO, "starting reset to %d", (int)flow->dest_client->id); + + // set resetting + flow->resetting = 1; + + peer_flow_drive_reset(flow); +} + +void peer_flow_drive_reset (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(!BTimer_IsRunning(&flow->reset_timer)) + + // try to free I/O + if (flow->have_io) { + if (PacketPassFairQueueFlow_IsBusy(&flow->qflow)) { + PacketPassFairQueueFlow_SetBusyHandler(&flow->qflow, (PacketPassFairQueue_handler_busy)peer_flow_reset_qflow_handler_busy, flow); + } else { + peer_flow_free_io(flow); + } + } + + // try to free opposite I/O + if (flow->opposite->have_io) { + if (PacketPassFairQueueFlow_IsBusy(&flow->opposite->qflow)) { + PacketPassFairQueueFlow_SetBusyHandler(&flow->opposite->qflow, (PacketPassFairQueue_handler_busy)peer_flow_reset_qflow_handler_busy, flow->opposite); + } else { + peer_flow_free_io(flow->opposite); + } + } + + // if we still got some I/O, or some client hasn't accepted yet, wait + if (flow->have_io || flow->opposite->have_io || !flow->accepted || !flow->opposite->accepted) { + return; + } + + // set reset timer + BReactor_SetTimer(&ss, &flow->reset_timer); +} + +void peer_flow_reset_qflow_handler_busy (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting || flow->opposite->resetting) + ASSERT(flow->have_io) + ASSERT(!PacketPassFairQueueFlow_IsBusy(&flow->qflow)) + + if (flow->resetting) { + peer_flow_drive_reset(flow); + } else { + peer_flow_drive_reset(flow->opposite); + } +} + +void peer_flow_reset_timer_handler (struct peer_flow *flow) +{ + ASSERT(flow->src_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->src_client->dying) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + ASSERT(flow->resetting) + ASSERT(!flow->opposite->resetting) + ASSERT(!flow->have_io) + ASSERT(!flow->opposite->have_io) + ASSERT(flow->accepted) + ASSERT(flow->opposite->accepted) + + client_log(flow->src_client, BLOG_INFO, "finally resetting to %d", (int)flow->dest_client->id); + + struct peer_know *know = flow->know; + struct peer_know *know_opposite = flow->opposite->know; + + // launch pair + if (!launch_pair(flow)) { + return; + } + + // remove old knows + uninform_know(know); + uninform_know(know_opposite); +} + +peerid_t new_client_id (void) +{ + ASSERT(clients_num < options.max_clients) + + for (int i = 0; i < options.max_clients; i++) { + peerid_t id = clients_nextid++; + if (!find_client_by_id(id)) { + return id; + } + } + + ASSERT(0) + return 42; +} + +struct client_data * find_client_by_id (peerid_t id) +{ + BAVLNode *node; + if (!(node = BAVL_LookupExact(&clients_tree, &id))) { + return NULL; + } + + return UPPER_OBJECT(node, struct client_data, tree_node); +} + +int clients_allowed (struct client_data *client1, struct client_data *client2) +{ + ASSERT(client1->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client1->dying) + ASSERT(client2->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client2->dying) + + if (!options.comm_predicate) { + return 1; + } + + // set values to compare against + comm_predicate_p1name = (client1->common_name ? client1->common_name : ""); + comm_predicate_p2name = (client2->common_name ? client2->common_name : ""); + BAddr_GetIPAddr(&client1->addr, &comm_predicate_p1addr); + BAddr_GetIPAddr(&client2->addr, &comm_predicate_p2addr); + + // evaluate predicate + int res = BPredicate_Eval(&comm_predicate); + if (res < 0) { + return 0; + } + + return res; +} + +int comm_predicate_func_p1name_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, comm_predicate_p1name)); +} + +int comm_predicate_func_p2name_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, comm_predicate_p2name)); +} + +int comm_predicate_func_p1addr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_WARNING, "failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &comm_predicate_p1addr); +} + +int comm_predicate_func_p2addr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_WARNING, "failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &comm_predicate_p2addr); +} + +int relay_allowed (struct client_data *client, struct client_data *relay) +{ + if (!options.relay_predicate) { + return 0; + } + + // set values to compare against + relay_predicate_pname = (client->common_name ? client->common_name : ""); + relay_predicate_rname = (relay->common_name ? relay->common_name : ""); + BAddr_GetIPAddr(&client->addr, &relay_predicate_paddr); + BAddr_GetIPAddr(&relay->addr, &relay_predicate_raddr); + + // evaluate predicate + int res = BPredicate_Eval(&relay_predicate); + if (res < 0) { + return 0; + } + + return res; +} + +int relay_predicate_func_pname_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, relay_predicate_pname)); +} + +int relay_predicate_func_rname_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + return (!strcmp(arg, relay_predicate_rname)); +} + +int relay_predicate_func_paddr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_ERROR, "paddr: failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &relay_predicate_paddr); +} + +int relay_predicate_func_raddr_cb (void *user, void **args) +{ + char *arg = (char *)args[0]; + + BIPAddr addr; + if (!BIPAddr_Resolve(&addr, arg, 1)) { + BLog(BLOG_ERROR, "raddr: failed to parse address"); + return -1; + } + + return BIPAddr_Compare(&addr, &relay_predicate_raddr); +} + +int peerid_comparator (void *unused, peerid_t *p1, peerid_t *p2) +{ + return B_COMPARE(*p1, *p2); +} + +struct peer_know * create_know (struct client_data *from, struct client_data *to, int relay_server, int relay_client) +{ + ASSERT(from->initstatus == INITSTATUS_COMPLETE) + ASSERT(!from->dying) + ASSERT(to->initstatus == INITSTATUS_COMPLETE) + ASSERT(!to->dying) + + // allocate structure + struct peer_know *k = (struct peer_know *)malloc(sizeof(*k)); + if (!k) { + return NULL; + } + + // init arguments + k->from = from; + k->to = to; + k->relay_server = relay_server; + k->relay_client = relay_client; + + // append to lists + LinkedList1_Append(&from->know_out_list, &k->from_node); + LinkedList1_Append(&to->know_in_list, &k->to_node); + + // init and set inform job to inform client 'from' about client 'to' + BPending_Init(&k->inform_job, BReactor_PendingGroup(&ss), (BPending_handler)know_inform_job_handler, k); + BPending_Set(&k->inform_job); + + // init uninform job + BPending_Init(&k->uninform_job, BReactor_PendingGroup(&ss), (BPending_handler)know_uninform_job_handler, k); + + return k; +} + +void remove_know (struct peer_know *k) +{ + // free uninform job + BPending_Free(&k->uninform_job); + + // free inform job + BPending_Free(&k->inform_job); + + // remove from lists + LinkedList1_Remove(&k->to->know_in_list, &k->to_node); + LinkedList1_Remove(&k->from->know_out_list, &k->from_node); + + // free structure + free(k); +} + +void know_inform_job_handler (struct peer_know *k) +{ + ASSERT(!k->from->dying) + ASSERT(!k->to->dying) + + client_send_newclient(k->from, k->to, k->relay_server, k->relay_client); + return; +} + +void uninform_know (struct peer_know *k) +{ + ASSERT(!k->from->dying) + + // if 'from' has not been informed about 'to' yet, remove know, otherwise + // schedule informing 'from' that 'to' is no more + if (BPending_IsSet(&k->inform_job)) { + remove_know(k); + } else { + BPending_Set(&k->uninform_job); + } +} + +void know_uninform_job_handler (struct peer_know *k) +{ + ASSERT(!k->from->dying) + ASSERT(!BPending_IsSet(&k->inform_job)) + + struct client_data *from = k->from; + struct client_data *to = k->to; + + // remove know + remove_know(k); + + // uninform + client_send_endclient(from, to->id); +} + +int launch_pair (struct peer_flow *flow_to) +{ + struct client_data *client = flow_to->src_client; + struct client_data *client2 = flow_to->dest_client; + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + ASSERT(client2->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client2->dying) + ASSERT(!flow_to->have_io) + ASSERT(!flow_to->opposite->have_io) + ASSERT(!BTimer_IsRunning(&flow_to->reset_timer)) + ASSERT(!BTimer_IsRunning(&flow_to->opposite->reset_timer)) + + // init I/O + if (!peer_flow_init_io(flow_to)) { + goto fail; + } + + // init opposite I/O + if (!peer_flow_init_io(flow_to->opposite)) { + goto fail; + } + + // determine relay relations + int relay_to = relay_allowed(client, client2); + int relay_from = relay_allowed(client2, client); + + // create know to + struct peer_know *know_to = create_know(client, client2, relay_to, relay_from); + if (!know_to) { + client_log(client, BLOG_ERROR, "failed to allocate know to %d", (int)client2->id); + goto fail; + } + + // create know from + struct peer_know *know_from = create_know(client2, client, relay_from, relay_to); + if (!know_from) { + client_log(client, BLOG_ERROR, "failed to allocate know from %d", (int)client2->id); + goto fail; + } + + // set know pointers in flows + flow_to->know = know_to; + flow_to->opposite->know = know_from; + + // set not accepted, or assume accepted for old version + flow_to->accepted = (flow_to->src_client->version <= SC_OLDVERSION_NOSSL); + flow_to->opposite->accepted = (flow_to->opposite->src_client->version <= SC_OLDVERSION_NOSSL); + + // set not resetting + flow_to->resetting = 0; + flow_to->opposite->resetting = 0; + + return 1; + +fail: + client_remove(client); + return 0; +} + +struct peer_flow * find_flow (struct client_data *client, peerid_t dest_id) +{ + ASSERT(client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!client->dying) + + BAVLNode *node = BAVL_LookupExact(&client->peer_out_flows_tree, &dest_id); + if (!node) { + return NULL; + } + struct peer_flow *flow = UPPER_OBJECT(node, struct peer_flow, src_tree_node); + + ASSERT(flow->dest_client->id == dest_id) + ASSERT(flow->dest_client->initstatus == INITSTATUS_COMPLETE) + ASSERT(!flow->dest_client->dying) + + return flow; +} diff --git a/external/badvpn_dns/server/server.h b/external/badvpn_dns/server/server.h new file mode 100644 index 00000000..66103a94 --- /dev/null +++ b/external/badvpn_dns/server/server.h @@ -0,0 +1,186 @@ +/** + * @file server.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// name of the program +#define PROGRAM_NAME "server" + +// maxiumum number of connected clients. Must be <=2^16. +#define DEFAULT_MAX_CLIENTS 30 +// client output control flow buffer size in packets +// it must hold: initdata, newclient's, endclient's (if other peers die when informing them) +// make it big enough to hold the initial packet burst (initdata, newclient's), +#define CLIENT_CONTROL_BUFFER_MIN_PACKETS (1 + 2*(MAX_CLIENTS - 1)) +// size of client-to-client buffers in packets +#define CLIENT_PEER_FLOW_BUFFER_MIN_PACKETS 10 +// after how long of not hearing anything from the client we disconnect it +#define CLIENT_NO_DATA_TIME_LIMIT 30000 +// SO_SNDBFUF socket option for clients +#define CLIENT_DEFAULT_SOCKET_SNDBUF 16384 +// reset time when a buffer runs out or when we get the resetpeer message +#define CLIENT_RESET_TIME 30000 + +// maxiumum listen addresses +#define MAX_LISTEN_ADDRS 16 + +//#define SIMULATE_OUT_OF_CONTROL_BUFFER 20 +//#define SIMULATE_OUT_OF_FLOW_BUFFER 100 + + +// performing SSL handshake +#define INITSTATUS_HANDSHAKE 1 +// waiting for clienthello +#define INITSTATUS_WAITHELLO 2 +// initialisation was complete +#define INITSTATUS_COMPLETE 3 + +#define INITSTATUS_HASLINK(status) ((status) == INITSTATUS_WAITHELLO || (status) == INITSTATUS_COMPLETE) + +struct client_data; +struct peer_know; + +struct peer_flow { + // source client + struct client_data *src_client; + // destination client + struct client_data *dest_client; + peerid_t dest_client_id; + // node in source client hash table (by destination), only when src_client != NULL + BAVLNode src_tree_node; + // node in source client list, only when src_client != NULL + LinkedList1Node src_list_node; + // node in destination client list + LinkedList1Node dest_list_node; + // output chain + int have_io; + PacketPassFairQueueFlow qflow; + PacketProtoFlow oflow; + BufferWriter *input; + int packet_len; + uint8_t *packet; + // reset timer + BTimer reset_timer; + // opposite flow + struct peer_flow *opposite; + // pair data + struct peer_know *know; + int accepted; + int resetting; +}; + +struct peer_know { + struct client_data *from; + struct client_data *to; + int relay_server; + int relay_client; + LinkedList1Node from_node; + LinkedList1Node to_node; + BPending inform_job; + BPending uninform_job; +}; + +struct client_data { + // socket + BConnection con; + BAddr addr; + + // SSL connection, if using SSL + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection sslcon; + + // initialization state + int initstatus; + + // client data if using SSL + uint8_t cert[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_len; + uint8_t cert_old[SCID_NEWCLIENT_MAX_CERT_LEN]; + int cert_old_len; + char *common_name; + + // client version + int version; + + // no data timer + BTimer disconnect_timer; + + // client ID + peerid_t id; + + // node in clients linked list + LinkedList1Node list_node; + // node in clients tree (by ID) + BAVLNode tree_node; + + // knowledge lists + LinkedList1 know_out_list; + LinkedList1 know_in_list; + + // flows from us + LinkedList1 peer_out_flows_list; + BAVL peer_out_flows_tree; + + // whether it's being removed + int dying; + BPending dying_job; + + // input + PacketProtoDecoder input_decoder; + PacketPassInterface input_interface; + + // output common + PacketStreamSender output_sender; + PacketPassPriorityQueue output_priorityqueue; + + // output control flow + PacketPassPriorityQueueFlow output_control_qflow; + PacketProtoFlow output_control_oflow; + BufferWriter *output_control_input; + int output_control_packet_len; + uint8_t *output_control_packet; + + // output peers flow + PacketPassPriorityQueueFlow output_peers_qflow; + PacketPassFairQueue output_peers_fairqueue; + LinkedList1 output_peers_flows; +}; diff --git a/external/badvpn_dns/server_connection/CMakeLists.txt b/external/badvpn_dns/server_connection/CMakeLists.txt new file mode 100644 index 00000000..4ae12ad2 --- /dev/null +++ b/external/badvpn_dns/server_connection/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SERVERCONNECTION_SOURCES + ServerConnection.c + SCKeepaliveSource.c +) +badvpn_add_library(server_conection "system;flow;flowextra;nspr_support" "${NSPR_LIBRARIES};${NSS_LIBRARIES}" "${SERVERCONNECTION_SOURCES}") diff --git a/external/badvpn_dns/server_connection/SCKeepaliveSource.c b/external/badvpn_dns/server_connection/SCKeepaliveSource.c new file mode 100644 index 00000000..7c6469a9 --- /dev/null +++ b/external/badvpn_dns/server_connection/SCKeepaliveSource.c @@ -0,0 +1,69 @@ +/** + * @file SCKeepaliveSource.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "SCKeepaliveSource.h" + +static void output_handler_recv (SCKeepaliveSource *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + + struct sc_header header; + header.type = htol8(SCID_KEEPALIVE); + memcpy(data, &header, sizeof(header)); + + PacketRecvInterface_Done(&o->output, sizeof(struct sc_header)); +} + +void SCKeepaliveSource_Init (SCKeepaliveSource *o, BPendingGroup *pg) +{ + // init output + PacketRecvInterface_Init(&o->output, sizeof(struct sc_header), (PacketRecvInterface_handler_recv)output_handler_recv, o, pg); + + DebugObject_Init(&o->d_obj); +} + +void SCKeepaliveSource_Free (SCKeepaliveSource *o) +{ + DebugObject_Free(&o->d_obj); + + // free output + PacketRecvInterface_Free(&o->output); +} + +PacketRecvInterface * SCKeepaliveSource_GetOutput (SCKeepaliveSource *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/server_connection/SCKeepaliveSource.h b/external/badvpn_dns/server_connection/SCKeepaliveSource.h new file mode 100644 index 00000000..1c0951e4 --- /dev/null +++ b/external/badvpn_dns/server_connection/SCKeepaliveSource.h @@ -0,0 +1,72 @@ +/** + * @file SCKeepaliveSource.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A {@link PacketRecvInterface} source which provides SCProto keepalive packets. + */ + +#ifndef BADVPN_SCKEEPALIVESOURCE_H +#define BADVPN_SCKEEPALIVESOURCE_H + +#include +#include + +/** + * A {@link PacketRecvInterface} source which provides SCProto keepalive packets. + */ +typedef struct { + DebugObject d_obj; + PacketRecvInterface output; +} SCKeepaliveSource; + +/** + * Initializes the object. + * + * @param o the object + * @param pg pending group + */ +void SCKeepaliveSource_Init (SCKeepaliveSource *o, BPendingGroup *pg); + +/** + * Frees the object. + * + * @param o the object + */ +void SCKeepaliveSource_Free (SCKeepaliveSource *o); + +/** + * Returns the output interface. + * The MTU of the output interface will be sizeof(struct sc_header). + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * SCKeepaliveSource_GetOutput (SCKeepaliveSource *o); + +#endif diff --git a/external/badvpn_dns/server_connection/ServerConnection.c b/external/badvpn_dns/server_connection/ServerConnection.c new file mode 100644 index 00000000..f07e2243 --- /dev/null +++ b/external/badvpn_dns/server_connection/ServerConnection.c @@ -0,0 +1,669 @@ +/** + * @file ServerConnection.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#define STATE_CONNECTING 1 +#define STATE_WAITINIT 2 +#define STATE_COMPLETE 3 + +static void report_error (ServerConnection *o); +static void connector_handler (ServerConnection *o, int is_error); +static void pending_handler (ServerConnection *o); +static SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey); +static void connection_handler (ServerConnection *o, int event); +static void sslcon_handler (ServerConnection *o, int event); +static void decoder_handler_error (ServerConnection *o); +static void input_handler_send (ServerConnection *o, uint8_t *data, int data_len); +static void packet_hello (ServerConnection *o, uint8_t *data, int data_len); +static void packet_newclient (ServerConnection *o, uint8_t *data, int data_len); +static void packet_endclient (ServerConnection *o, uint8_t *data, int data_len); +static void packet_inmsg (ServerConnection *o, uint8_t *data, int data_len); +static int start_packet (ServerConnection *o, void **data, int len); +static void end_packet (ServerConnection *o, uint8_t type); +static void newclient_job_handler (ServerConnection *o); + +void report_error (ServerConnection *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->user)) +} + +void connector_handler (ServerConnection *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_CONNECTING) + ASSERT(!o->buffers_released) + + // check connection attempt result + if (is_error) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + BLog(BLOG_NOTICE, "connected"); + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + // init connection interfaces + BConnection_SendAsync_Init(&o->con); + BConnection_RecvAsync_Init(&o->con); + + StreamPassInterface *send_iface = BConnection_SendAsync_GetIf(&o->con); + StreamRecvInterface *recv_iface = BConnection_RecvAsync_GetIf(&o->con); + + if (o->have_ssl) { + // create bottom NSPR file descriptor + if (!BSSLConnection_MakeBackend(&o->bottom_prfd, send_iface, recv_iface, o->twd, o->ssl_flags)) { + BLog(BLOG_ERROR, "BSSLConnection_MakeBackend failed"); + goto fail0a; + } + + // create SSL file descriptor from the bottom NSPR file descriptor + if (!(o->ssl_prfd = SSL_ImportFD(NULL, &o->bottom_prfd))) { + BLog(BLOG_ERROR, "SSL_ImportFD failed"); + ASSERT_FORCE(PR_Close(&o->bottom_prfd) == PR_SUCCESS) + goto fail0a; + } + + // set client mode + if (SSL_ResetHandshake(o->ssl_prfd, PR_FALSE) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_ResetHandshake failed"); + goto fail1; + } + + // set server name + if (SSL_SetURL(o->ssl_prfd, o->server_name) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_SetURL failed"); + goto fail1; + } + + // set client certificate callback + if (SSL_GetClientAuthDataHook(o->ssl_prfd, (SSLGetClientAuthData)client_auth_data_callback, o) != SECSuccess) { + BLog(BLOG_ERROR, "SSL_GetClientAuthDataHook failed"); + goto fail1; + } + + // init BSSLConnection + BSSLConnection_Init(&o->sslcon, o->ssl_prfd, 0, BReactor_PendingGroup(o->reactor), o, (BSSLConnection_handler)sslcon_handler); + + send_iface = BSSLConnection_GetSendIf(&o->sslcon); + recv_iface = BSSLConnection_GetRecvIf(&o->sslcon); + } + + // init input chain + PacketPassInterface_Init(&o->input_interface, SC_MAX_ENC, (PacketPassInterface_handler_send)input_handler_send, o, BReactor_PendingGroup(o->reactor)); + if (!PacketProtoDecoder_Init(&o->input_decoder, recv_iface, &o->input_interface, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + // set job to send hello + // this needs to be in here because hello sending must be done after sending started (so we can write into the send buffer), + // but before receiving started (so we don't get into conflict with the user sending packets) + BPending_Init(&o->start_job, BReactor_PendingGroup(o->reactor), (BPending_handler)pending_handler, o); + BPending_Set(&o->start_job); + + // init keepalive output branch + SCKeepaliveSource_Init(&o->output_ka_zero, BReactor_PendingGroup(o->reactor)); + PacketProtoEncoder_Init(&o->output_ka_encoder, SCKeepaliveSource_GetOutput(&o->output_ka_zero), BReactor_PendingGroup(o->reactor)); + + // init output common + + // init sender + PacketStreamSender_Init(&o->output_sender, send_iface, PACKETPROTO_ENCLEN(SC_MAX_ENC), BReactor_PendingGroup(o->reactor)); + + // init keepalives + if (!KeepaliveIO_Init(&o->output_keepaliveio, o->reactor, PacketStreamSender_GetInput(&o->output_sender), PacketProtoEncoder_GetOutput(&o->output_ka_encoder), o->keepalive_interval)) { + BLog(BLOG_ERROR, "KeepaliveIO_Init failed"); + goto fail3; + } + + // init queue + PacketPassPriorityQueue_Init(&o->output_queue, KeepaliveIO_GetInput(&o->output_keepaliveio), BReactor_PendingGroup(o->reactor), 0); + + // init output local flow + + // init queue flow + PacketPassPriorityQueueFlow_Init(&o->output_local_qflow, &o->output_queue, 0); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init(&o->output_local_oflow, SC_MAX_ENC, o->buffer_size, PacketPassPriorityQueueFlow_GetInput(&o->output_local_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail4; + } + o->output_local_if = PacketProtoFlow_GetInput(&o->output_local_oflow); + + // have no output packet + o->output_local_packet_len = -1; + + // init output user flow + PacketPassPriorityQueueFlow_Init(&o->output_user_qflow, &o->output_queue, 1); + + // update state + o->state = STATE_WAITINIT; + + return; + +fail4: + PacketPassPriorityQueueFlow_Free(&o->output_local_qflow); + PacketPassPriorityQueue_Free(&o->output_queue); + KeepaliveIO_Free(&o->output_keepaliveio); +fail3: + PacketStreamSender_Free(&o->output_sender); + PacketProtoEncoder_Free(&o->output_ka_encoder); + SCKeepaliveSource_Free(&o->output_ka_zero); + BPending_Free(&o->start_job); + PacketProtoDecoder_Free(&o->input_decoder); +fail2: + PacketPassInterface_Free(&o->input_interface); + if (o->have_ssl) { + BSSLConnection_Free(&o->sslcon); +fail1: + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + } +fail0a: + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + BConnection_Free(&o->con); +fail0: + // report error + report_error(o); +} + +void pending_handler (ServerConnection *o) +{ + ASSERT(o->state == STATE_WAITINIT) + ASSERT(!o->buffers_released) + DebugObject_Access(&o->d_obj); + + // send hello + struct sc_client_hello omsg; + void *packet; + if (!start_packet(o, &packet, sizeof(omsg))) { + BLog(BLOG_ERROR, "no buffer for hello"); + report_error(o); + return; + } + omsg.version = htol16(SC_VERSION); + memcpy(packet, &omsg, sizeof(omsg)); + end_packet(o, SCID_CLIENTHELLO); +} + +SECStatus client_auth_data_callback (ServerConnection *o, PRFileDesc *fd, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey) +{ + ASSERT(o->have_ssl) + DebugObject_Access(&o->d_obj); + + CERTCertificate *newcert; + if (!(newcert = CERT_DupCertificate(o->client_cert))) { + return SECFailure; + } + + SECKEYPrivateKey *newkey; + if (!(newkey = SECKEY_CopyPrivateKey(o->client_key))) { + CERT_DestroyCertificate(newcert); + return SECFailure; + } + + *pRetCert = newcert; + *pRetKey = newkey; + return SECSuccess; +} + +void connection_handler (ServerConnection *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "connection closed"); + } else { + BLog(BLOG_INFO, "connection error"); + } + + report_error(o); + return; +} + +void sslcon_handler (ServerConnection *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_ssl) + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + ASSERT(event == BSSLCONNECTION_EVENT_ERROR) + + BLog(BLOG_ERROR, "SSL error"); + + report_error(o); + return; +} + +void decoder_handler_error (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + + BLog(BLOG_ERROR, "decoder error"); + + report_error(o); + return; +} + +void input_handler_send (ServerConnection *o, uint8_t *data, int data_len) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(!o->buffers_released) + ASSERT(data_len >= 0) + ASSERT(data_len <= SC_MAX_ENC) + DebugObject_Access(&o->d_obj); + + // accept packet + PacketPassInterface_Done(&o->input_interface); + + // parse header + if (data_len < sizeof(struct sc_header)) { + BLog(BLOG_ERROR, "packet too short (no sc header)"); + report_error(o); + return; + } + struct sc_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t type = ltoh8(header.type); + + // call appropriate handler based on packet type + switch (type) { + case SCID_SERVERHELLO: + packet_hello(o, data, data_len); + return; + case SCID_NEWCLIENT: + packet_newclient(o, data, data_len); + return; + case SCID_ENDCLIENT: + packet_endclient(o, data, data_len); + return; + case SCID_INMSG: + packet_inmsg(o, data, data_len); + return; + default: + BLog(BLOG_ERROR, "unknown packet type %d", (int)type); + report_error(o); + return; + } +} + +void packet_hello (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_WAITINIT) { + BLog(BLOG_ERROR, "hello: not expected"); + report_error(o); + return; + } + + if (data_len != sizeof(struct sc_server_hello)) { + BLog(BLOG_ERROR, "hello: invalid length"); + report_error(o); + return; + } + struct sc_server_hello msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // change state + o->state = STATE_COMPLETE; + + // report + o->handler_ready(o->user, id, msg.clientAddr); + return; +} + +void packet_newclient (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "newclient: not expected"); + report_error(o); + return; + } + + if (data_len < sizeof(struct sc_server_newclient) || data_len > sizeof(struct sc_server_newclient) + SCID_NEWCLIENT_MAX_CERT_LEN) { + BLog(BLOG_ERROR, "newclient: invalid length"); + report_error(o); + return; + } + + struct sc_server_newclient msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // schedule reporting new client + o->newclient_data = data; + o->newclient_data_len = data_len; + BPending_Set(&o->newclient_job); + + // send acceptpeer + struct sc_client_acceptpeer omsg; + void *packet; + if (!start_packet(o, &packet, sizeof(omsg))) { + BLog(BLOG_ERROR, "newclient: out of buffer for acceptpeer"); + report_error(o); + return; + } + omsg.clientid = htol16(id); + memcpy(packet, &omsg, sizeof(omsg)); + end_packet(o, SCID_ACCEPTPEER); +} + +void packet_endclient (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "endclient: not expected"); + report_error(o); + return; + } + + if (data_len != sizeof(struct sc_server_endclient)) { + BLog(BLOG_ERROR, "endclient: invalid length"); + report_error(o); + return; + } + + struct sc_server_endclient msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + + // report + o->handler_endclient(o->user, id); + return; +} + +void packet_inmsg (ServerConnection *o, uint8_t *data, int data_len) +{ + if (o->state != STATE_COMPLETE) { + BLog(BLOG_ERROR, "inmsg: not expected"); + report_error(o); + return; + } + + if (data_len < sizeof(struct sc_server_inmsg)) { + BLog(BLOG_ERROR, "inmsg: missing header"); + report_error(o); + return; + } + + if (data_len > sizeof(struct sc_server_inmsg) + SC_MAX_MSGLEN) { + BLog(BLOG_ERROR, "inmsg: too long"); + report_error(o); + return; + } + + struct sc_server_inmsg msg; + memcpy(&msg, data, sizeof(msg)); + peerid_t peer_id = ltoh16(msg.clientid); + uint8_t *payload = data + sizeof(struct sc_server_inmsg); + int payload_len = data_len - sizeof(struct sc_server_inmsg); + + // report + o->handler_message(o->user, peer_id, payload, payload_len); + return; +} + +int start_packet (ServerConnection *o, void **data, int len) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(o->output_local_packet_len == -1) + ASSERT(len >= 0) + ASSERT(len <= SC_MAX_PAYLOAD) + ASSERT(data || len == 0) + + // obtain memory location + if (!BufferWriter_StartPacket(o->output_local_if, &o->output_local_packet)) { + BLog(BLOG_ERROR, "out of buffer"); + return 0; + } + + o->output_local_packet_len = len; + + if (data) { + *data = o->output_local_packet + sizeof(struct sc_header); + } + + return 1; +} + +void end_packet (ServerConnection *o, uint8_t type) +{ + ASSERT(o->state >= STATE_WAITINIT) + ASSERT(o->output_local_packet_len >= 0) + ASSERT(o->output_local_packet_len <= SC_MAX_PAYLOAD) + + // write header + struct sc_header header; + header.type = htol8(type); + memcpy(o->output_local_packet, &header, sizeof(header)); + + // finish writing packet + BufferWriter_EndPacket(o->output_local_if, sizeof(struct sc_header) + o->output_local_packet_len); + + o->output_local_packet_len = -1; +} + +int ServerConnection_Init ( + ServerConnection *o, + BReactor *reactor, + BThreadWorkDispatcher *twd, + BAddr addr, + int keepalive_interval, + int buffer_size, + int have_ssl, + int ssl_flags, + CERTCertificate *client_cert, + SECKEYPrivateKey *client_key, + const char *server_name, + void *user, + ServerConnection_handler_error handler_error, + ServerConnection_handler_ready handler_ready, + ServerConnection_handler_newclient handler_newclient, + ServerConnection_handler_endclient handler_endclient, + ServerConnection_handler_message handler_message +) +{ + ASSERT(keepalive_interval > 0) + ASSERT(buffer_size > 0) + ASSERT(have_ssl == 0 || have_ssl == 1) + ASSERT(!have_ssl || server_name) + + // init arguments + o->reactor = reactor; + o->twd = twd; + o->keepalive_interval = keepalive_interval; + o->buffer_size = buffer_size; + o->have_ssl = have_ssl; + if (have_ssl) { + o->ssl_flags = ssl_flags; + o->client_cert = client_cert; + o->client_key = client_key; + } + o->user = user; + o->handler_error = handler_error; + o->handler_ready = handler_ready; + o->handler_newclient = handler_newclient; + o->handler_endclient = handler_endclient; + o->handler_message = handler_message; + + o->server_name = NULL; + if (have_ssl && !(o->server_name = b_strdup(server_name))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "BConnection_AddressSupported failed"); + goto fail1; + } + + // init connector + if (!BConnector_Init(&o->connector, addr, o->reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail1; + } + + // init newclient job + BPending_Init(&o->newclient_job, BReactor_PendingGroup(o->reactor), (BPending_handler)newclient_job_handler, o); + + // set state + o->state = STATE_CONNECTING; + o->buffers_released = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + free(o->server_name); +fail0: + return 0; +} + +void ServerConnection_Free (ServerConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + if (o->state > STATE_CONNECTING) { + // allow freeing queue flows + PacketPassPriorityQueue_PrepareFree(&o->output_queue); + + // stop using any buffers before they get freed + if (o->have_ssl && !o->buffers_released) { + BSSLConnection_ReleaseBuffers(&o->sslcon); + } + + // free output user flow + PacketPassPriorityQueueFlow_Free(&o->output_user_qflow); + + // free output local flow + PacketProtoFlow_Free(&o->output_local_oflow); + PacketPassPriorityQueueFlow_Free(&o->output_local_qflow); + + // free output common + PacketPassPriorityQueue_Free(&o->output_queue); + KeepaliveIO_Free(&o->output_keepaliveio); + PacketStreamSender_Free(&o->output_sender); + + // free output keep-alive branch + PacketProtoEncoder_Free(&o->output_ka_encoder); + SCKeepaliveSource_Free(&o->output_ka_zero); + + // free job + BPending_Free(&o->start_job); + + // free input chain + PacketProtoDecoder_Free(&o->input_decoder); + PacketPassInterface_Free(&o->input_interface); + + // free SSL + if (o->have_ssl) { + BSSLConnection_Free(&o->sslcon); + ASSERT_FORCE(PR_Close(o->ssl_prfd) == PR_SUCCESS) + } + + // free connection interfaces + BConnection_RecvAsync_Free(&o->con); + BConnection_SendAsync_Free(&o->con); + + // free connection + BConnection_Free(&o->con); + } + + // free newclient job + BPending_Free(&o->newclient_job); + + // free connector + BConnector_Free(&o->connector); + + // free server name + free(o->server_name); +} + +void ServerConnection_ReleaseBuffers (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->buffers_released) + + if (o->state > STATE_CONNECTING && o->have_ssl) { + BSSLConnection_ReleaseBuffers(&o->sslcon); + } + + o->buffers_released = 1; +} + +PacketPassInterface * ServerConnection_GetSendInterface (ServerConnection *o) +{ + ASSERT(o->state == STATE_COMPLETE) + DebugError_AssertNoError(&o->d_err); + DebugObject_Access(&o->d_obj); + + return PacketPassPriorityQueueFlow_GetInput(&o->output_user_qflow); +} + +void newclient_job_handler (ServerConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_COMPLETE) + + struct sc_server_newclient msg; + memcpy(&msg, o->newclient_data, sizeof(msg)); + peerid_t id = ltoh16(msg.id); + int flags = ltoh16(msg.flags); + + uint8_t *cert_data = o->newclient_data + sizeof(msg); + int cert_len = o->newclient_data_len - sizeof(msg); + + // report new client + o->handler_newclient(o->user, id, flags, cert_data, cert_len); + return; +} diff --git a/external/badvpn_dns/server_connection/ServerConnection.h b/external/badvpn_dns/server_connection/ServerConnection.h new file mode 100644 index 00000000..1245c846 --- /dev/null +++ b/external/badvpn_dns/server_connection/ServerConnection.h @@ -0,0 +1,289 @@ +/** + * @file ServerConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object used to communicate with a VPN chat server. + */ + +#ifndef BADVPN_SERVERCONNECTION_SERVERCONNECTION_H +#define BADVPN_SERVERCONNECTION_SERVERCONNECTION_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Handler function invoked when an error occurs. + * The object must be freed from withing this function. + * + * @param user value passed to {@link ServerConnection_Init} + */ +typedef void (*ServerConnection_handler_error) (void *user); + +/** + * Handler function invoked when the server becomes ready, i.e. + * the hello packet has been received. + * The object was in not ready state before. + * The object enters ready state before the handler is invoked. + * + * @param user value passed to {@link ServerConnection_Init} + * @param my_id our ID as reported by the server + * @param ext_ip the clientAddr field in the server's hello packet + */ +typedef void (*ServerConnection_handler_ready) (void *user, peerid_t my_id, uint32_t ext_ip); + +/** + * Handler function invoked when a newclient packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer + * @param flags flags field from the newclient message + * @param cert peer's certificate (if any) + * @param cert_len certificate length. Will be >=0. + */ +typedef void (*ServerConnection_handler_newclient) (void *user, peerid_t peer_id, int flags, const uint8_t *cert, int cert_len); + +/** + * Handler function invoked when an enclient packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer + */ +typedef void (*ServerConnection_handler_endclient) (void *user, peerid_t peer_id); + +/** + * Handler function invoked when an inmsg packet is received. + * The object was in ready state. + * + * @param user value passed to {@link ServerConnection_Init} + * @param peer_id ID of the peer from which the message came + * @param data message payload + * @param data_len message length. Will be >=0. + */ +typedef void (*ServerConnection_handler_message) (void *user, peerid_t peer_id, uint8_t *data, int data_len); + +/** + * Object used to communicate with a VPN chat server. + */ +typedef struct { + // global resources + BReactor *reactor; + BThreadWorkDispatcher *twd; + + // keepalive interval + int keepalive_interval; + + // send buffer size + int buffer_size; + + // whether we use SSL + int have_ssl; + + // ssl flags + int ssl_flags; + + // client certificate if using SSL + CERTCertificate *client_cert; + + // client private key if using SSL + SECKEYPrivateKey *client_key; + + // server name if using SSL + char *server_name; + + // handlers + void *user; + ServerConnection_handler_error handler_error; + ServerConnection_handler_ready handler_ready; + ServerConnection_handler_newclient handler_newclient; + ServerConnection_handler_endclient handler_endclient; + ServerConnection_handler_message handler_message; + + // socket + BConnector connector; + BConnection con; + + // job to report new client after sending acceptpeer + BPending newclient_job; + uint8_t *newclient_data; + int newclient_data_len; + + // state + int state; + int buffers_released; + + // whether an error is being reported + int error; + + // defined when state > SERVERCONNECTION_STATE_CONNECTING + + // SSL file descriptor, defined only if using SSL + PRFileDesc bottom_prfd; + PRFileDesc *ssl_prfd; + BSSLConnection sslcon; + + // input + PacketProtoDecoder input_decoder; + PacketPassInterface input_interface; + + // keepalive output branch + SCKeepaliveSource output_ka_zero; + PacketProtoEncoder output_ka_encoder; + + // output common + PacketPassPriorityQueue output_queue; + KeepaliveIO output_keepaliveio; + PacketStreamSender output_sender; + + // output local flow + int output_local_packet_len; + uint8_t *output_local_packet; + BufferWriter *output_local_if; + PacketProtoFlow output_local_oflow; + PacketPassPriorityQueueFlow output_local_qflow; + + // output user flow + PacketPassPriorityQueueFlow output_user_qflow; + + // job to start client I/O + BPending start_job; + + DebugError d_err; + DebugObject d_obj; +} ServerConnection; + +/** + * Initializes the object. + * The object is initialized in not ready state. + * {@link BLog_Init} must have been done. + * {@link BNetwork_GlobalInit} must have been done. + * {@link BSSLConnection_GlobalInit} must have been done if using SSL. + * + * @param o the object + * @param reactor {@link BReactor} we live in + * @param twd thread work dispatcher. May be NULL if ssl_flags does not request performing SSL + * operations in threads. + * @param addr address to connect to + * @param keepalive_interval keep-alive sending interval. Must be >0. + * @param buffer_size minimum size of send buffer in number of packets. Must be >0. + * @param have_ssl whether to use SSL for connecting to the server. Must be 1 or 0. + * @param ssl_flags flags passed down to {@link BSSLConnection_MakeBackend}. May be used to + * request performing SSL operations in threads. + * @param client_cert if using SSL, client certificate to use. Must remain valid as + * long as this object is alive. + * @param client_key if using SSL, prvate ket to use. Must remain valid as + * long as this object is alive. + * @param server_name if using SSL, the name of the server. The string is copied. + * @param user value passed to callback functions + * @param handler_error error handler. The object must be freed from within the error + * handler before doing anything else with this object. + * @param handler_ready handler when the server becomes ready, i.e. the hello message has + * been received. + * @param handler_newclient handler when a newclient message has been received + * @param handler_endclient handler when an endclient message has been received + * @param handler_message handler when a peer message has been reveived + * @return 1 on success, 0 on failure + */ +int ServerConnection_Init ( + ServerConnection *o, + BReactor *reactor, + BThreadWorkDispatcher *twd, + BAddr addr, + int keepalive_interval, + int buffer_size, + int have_ssl, + int ssl_flags, + CERTCertificate *client_cert, + SECKEYPrivateKey *client_key, + const char *server_name, + void *user, + ServerConnection_handler_error handler_error, + ServerConnection_handler_ready handler_ready, + ServerConnection_handler_newclient handler_newclient, + ServerConnection_handler_endclient handler_endclient, + ServerConnection_handler_message handler_message +) WARN_UNUSED; + +/** + * Frees the object. + * {@link ServerConnection_ReleaseBuffers} must have been called if the + * send interface obtained from {@link ServerConnection_GetSendInterface} + * was used. + * + * @param o the object + */ +void ServerConnection_Free (ServerConnection *o); + +/** + * Stops using any buffers passed to the send interface obtained from + * {@link ServerConnection_GetSendInterface}. If the send interface + * has been used, this must be called at appropriate time before this + * object is freed. + */ +void ServerConnection_ReleaseBuffers (ServerConnection *o); + +/** + * Returns an interface for sending data to the server (just one). + * This goes directly into the link (i.e. TCP, possibly via SSL), so packets + * need to be manually encoded according to PacketProto. + * The interface must not be used after an error was reported. + * The object must be in ready state. + * Must not be called from the error handler. + * + * @param o the object + * @return the interface + */ +PacketPassInterface * ServerConnection_GetSendInterface (ServerConnection *o); + +#endif diff --git a/external/badvpn_dns/socksclient/BSocksClient.c b/external/badvpn_dns/socksclient/BSocksClient.c new file mode 100644 index 00000000..21415af4 --- /dev/null +++ b/external/badvpn_dns/socksclient/BSocksClient.c @@ -0,0 +1,608 @@ +/** + * @file BSocksClient.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include + +#define STATE_CONNECTING 1 +#define STATE_SENDING_HELLO 2 +#define STATE_SENT_HELLO 3 +#define STATE_SENDING_PASSWORD 10 +#define STATE_SENT_PASSWORD 11 +#define STATE_SENDING_REQUEST 4 +#define STATE_SENT_REQUEST 5 +#define STATE_RECEIVED_REPLY_HEADER 6 +#define STATE_UP 7 + +static void report_error (BSocksClient *o, int error); +static void init_control_io (BSocksClient *o); +static void free_control_io (BSocksClient *o); +static void init_up_io (BSocksClient *o); +static void free_up_io (BSocksClient *o); +static int reserve_buffer (BSocksClient *o, bsize_t size); +static void start_receive (BSocksClient *o, uint8_t *dest, int total); +static void do_receive (BSocksClient *o); +static void connector_handler (BSocksClient* o, int is_error); +static void connection_handler (BSocksClient* o, int event); +static void recv_handler_done (BSocksClient *o, int data_len); +static void send_handler_done (BSocksClient *o); +static void auth_finished (BSocksClient *p); + +void report_error (BSocksClient *o, int error) +{ + DEBUGERROR(&o->d_err, o->handler(o->user, error)) +} + +void init_control_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + o->control.recv_if = BConnection_RecvAsync_GetIf(&o->con); + StreamRecvInterface_Receiver_Init(o->control.recv_if, (StreamRecvInterface_handler_done)recv_handler_done, o); + + // init sending + BConnection_SendAsync_Init(&o->con); + PacketStreamSender_Init(&o->control.send_sender, BConnection_SendAsync_GetIf(&o->con), INT_MAX, BReactor_PendingGroup(o->reactor)); + o->control.send_if = PacketStreamSender_GetInput(&o->control.send_sender); + PacketPassInterface_Sender_Init(o->control.send_if, (PacketPassInterface_handler_done)send_handler_done, o); +} + +void free_control_io (BSocksClient *o) +{ + // free sending + PacketStreamSender_Free(&o->control.send_sender); + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +void init_up_io (BSocksClient *o) +{ + // init receiving + BConnection_RecvAsync_Init(&o->con); + + // init sending + BConnection_SendAsync_Init(&o->con); +} + +void free_up_io (BSocksClient *o) +{ + // free sending + BConnection_SendAsync_Free(&o->con); + + // free receiving + BConnection_RecvAsync_Free(&o->con); +} + +int reserve_buffer (BSocksClient *o, bsize_t size) +{ + if (size.is_overflow) { + BLog(BLOG_ERROR, "size overflow"); + return 0; + } + + char *buffer = (char *)BRealloc(o->buffer, size.value); + if (!buffer) { + BLog(BLOG_ERROR, "BRealloc failed"); + return 0; + } + + o->buffer = buffer; + + return 1; +} + +void start_receive (BSocksClient *o, uint8_t *dest, int total) +{ + ASSERT(total > 0) + + o->control.recv_dest = dest; + o->control.recv_len = 0; + o->control.recv_total = total; + + do_receive(o); +} + +void do_receive (BSocksClient *o) +{ + ASSERT(o->control.recv_len < o->control.recv_total) + + StreamRecvInterface_Receiver_Recv(o->control.recv_if, o->control.recv_dest + o->control.recv_len, o->control.recv_total - o->control.recv_len); +} + +void connector_handler (BSocksClient* o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state == STATE_CONNECTING) + + // check connection result + if (is_error) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // init connection + if (!BConnection_Init(&o->con, BConnection_source_connector(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail0; + } + + BLog(BLOG_DEBUG, "connected"); + + // init control I/O + init_control_io(o); + + // check number of methods + if (o->num_auth_info == 0 || o->num_auth_info > 255) { + BLog(BLOG_ERROR, "invalid number of authentication methods"); + goto fail1; + } + + // allocate buffer for sending hello + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_client_hello_header)), + bsize_mul( + bsize_fromsize(o->num_auth_info), + bsize_fromsize(sizeof(struct socks_client_hello_method)) + ) + ); + if (!reserve_buffer(o, size)) { + goto fail1; + } + + // write hello header + struct socks_client_hello_header header; + header.ver = hton8(SOCKS_VERSION); + header.nmethods = hton8(o->num_auth_info); + memcpy(o->buffer, &header, sizeof(header)); + + // write hello methods + for (size_t i = 0; i < o->num_auth_info; i++) { + struct socks_client_hello_method method; + method.method = hton8(o->auth_info[i].auth_type); + memcpy(o->buffer + sizeof(header) + i * sizeof(method), &method, sizeof(method)); + } + + // send + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_HELLO; + + return; + +fail1: + free_control_io(o); + BConnection_Free(&o->con); +fail0: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void connection_handler (BSocksClient* o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->state != STATE_CONNECTING) + + if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED); + return; + } + + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; +} + +void recv_handler_done (BSocksClient *o, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= o->control.recv_total - o->control.recv_len) + DebugObject_Access(&o->d_obj); + + o->control.recv_len += data_len; + + if (o->control.recv_len < o->control.recv_total) { + do_receive(o); + return; + } + + switch (o->state) { + case STATE_SENT_HELLO: { + BLog(BLOG_DEBUG, "received hello"); + + struct socks_server_hello imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + size_t auth_index; + for (auth_index = 0; auth_index < o->num_auth_info; auth_index++) { + if (o->auth_info[auth_index].auth_type == ntoh8(imsg.method)) { + break; + } + } + + if (auth_index == o->num_auth_info) { + BLog(BLOG_NOTICE, "server didn't accept any authentication method"); + goto fail; + } + + const struct BSocksClient_auth_info *ai = &o->auth_info[auth_index]; + + switch (ai->auth_type) { + case SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED: { + BLog(BLOG_DEBUG, "no authentication"); + + auth_finished(o); + } break; + + case SOCKS_METHOD_USERNAME_PASSWORD: { + BLog(BLOG_DEBUG, "password authentication"); + + if (ai->password.username_len == 0 || ai->password.username_len > 255 || + ai->password.password_len == 0 || ai->password.password_len > 255 + ) { + BLog(BLOG_NOTICE, "invalid username/password length"); + goto fail; + } + + // allocate password packet + bsize_t size = bsize_fromsize(1 + 1 + ai->password.username_len + 1 + ai->password.password_len); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // write password packet + char *ptr = o->buffer; + *ptr++ = 1; + *ptr++ = ai->password.username_len; + memcpy(ptr, ai->password.username, ai->password.username_len); + ptr += ai->password.username_len; + *ptr++ = ai->password.password_len; + memcpy(ptr, ai->password.password, ai->password.password_len); + ptr += ai->password.password_len; + + // start sending + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_PASSWORD; + } break; + + default: ASSERT(0); + } + } break; + + case STATE_SENT_REQUEST: { + BLog(BLOG_DEBUG, "received reply header"); + + struct socks_reply_header imsg; + memcpy(&imsg, o->buffer, sizeof(imsg)); + + if (ntoh8(imsg.ver) != SOCKS_VERSION) { + BLog(BLOG_NOTICE, "wrong version"); + goto fail; + } + + if (ntoh8(imsg.rep) != SOCKS_REP_SUCCEEDED) { + BLog(BLOG_NOTICE, "reply not successful"); + goto fail; + } + + int addr_len; + switch (ntoh8(imsg.atyp)) { + case SOCKS_ATYP_IPV4: + addr_len = sizeof(struct socks_addr_ipv4); + break; + case SOCKS_ATYP_IPV6: + addr_len = sizeof(struct socks_addr_ipv6); + break; + default: + BLog(BLOG_NOTICE, "reply has unknown address type"); + goto fail; + } + + // receive the rest of the reply + start_receive(o, (uint8_t *)o->buffer + sizeof(imsg), addr_len); + + // set state + o->state = STATE_RECEIVED_REPLY_HEADER; + } break; + + case STATE_SENT_PASSWORD: { + BLog(BLOG_DEBUG, "received password reply"); + + if (o->buffer[0] != 1) { + BLog(BLOG_NOTICE, "password reply has unknown version"); + goto fail; + } + + if (o->buffer[1] != 0) { + BLog(BLOG_NOTICE, "password reply is negative"); + goto fail; + } + + auth_finished(o); + } break; + + case STATE_RECEIVED_REPLY_HEADER: { + BLog(BLOG_DEBUG, "received reply rest"); + + // free buffer + BFree(o->buffer); + o->buffer = NULL; + + // free control I/O + free_control_io(o); + + // init up I/O + init_up_io(o); + + // set state + o->state = STATE_UP; + + // call handler + o->handler(o->user, BSOCKSCLIENT_EVENT_UP); + return; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void send_handler_done (BSocksClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->buffer) + + switch (o->state) { + case STATE_SENDING_HELLO: { + BLog(BLOG_DEBUG, "sent hello"); + + // allocate buffer for receiving hello + bsize_t size = bsize_fromsize(sizeof(struct socks_server_hello)); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive hello + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_HELLO; + } break; + + case STATE_SENDING_REQUEST: { + BLog(BLOG_DEBUG, "sent request"); + + // allocate buffer for receiving reply + bsize_t size = bsize_add( + bsize_fromsize(sizeof(struct socks_reply_header)), + bsize_max(bsize_fromsize(sizeof(struct socks_addr_ipv4)), bsize_fromsize(sizeof(struct socks_addr_ipv6))) + ); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, sizeof(struct socks_reply_header)); + + // set state + o->state = STATE_SENT_REQUEST; + } break; + + case STATE_SENDING_PASSWORD: { + BLog(BLOG_DEBUG, "send password"); + + // allocate buffer for receiving reply + bsize_t size = bsize_fromsize(2); + if (!reserve_buffer(o, size)) { + goto fail; + } + + // receive reply header + start_receive(o, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENT_PASSWORD; + } break; + + default: + ASSERT(0); + } + + return; + +fail: + report_error(o, BSOCKSCLIENT_EVENT_ERROR); +} + +void auth_finished (BSocksClient *o) +{ + // allocate request buffer + bsize_t size = bsize_fromsize(sizeof(struct socks_request_header)); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4))); break; + case BADDR_TYPE_IPV6: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6))); break; + } + if (!reserve_buffer(o, size)) { + report_error(o, BSOCKSCLIENT_EVENT_ERROR); + return; + } + + // write request + struct socks_request_header header; + header.ver = hton8(SOCKS_VERSION); + header.cmd = hton8(SOCKS_CMD_CONNECT); + header.rsv = hton8(0); + switch (o->dest_addr.type) { + case BADDR_TYPE_IPV4: { + header.atyp = hton8(SOCKS_ATYP_IPV4); + struct socks_addr_ipv4 addr; + addr.addr = o->dest_addr.ipv4.ip; + addr.port = o->dest_addr.ipv4.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + case BADDR_TYPE_IPV6: { + header.atyp = hton8(SOCKS_ATYP_IPV6); + struct socks_addr_ipv6 addr; + memcpy(addr.addr, o->dest_addr.ipv6.ip, sizeof(o->dest_addr.ipv6.ip)); + addr.port = o->dest_addr.ipv6.port; + memcpy(o->buffer + sizeof(header), &addr, sizeof(addr)); + } break; + default: + ASSERT(0); + } + memcpy(o->buffer, &header, sizeof(header)); + + // send request + PacketPassInterface_Sender_Send(o->control.send_if, (uint8_t *)o->buffer, size.value); + + // set state + o->state = STATE_SENDING_REQUEST; +} + +struct BSocksClient_auth_info BSocksClient_auth_none (void) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED; + return info; +} + +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len) +{ + struct BSocksClient_auth_info info; + info.auth_type = SOCKS_METHOD_USERNAME_PASSWORD; + info.password.username = username; + info.password.username_len = username_len; + info.password.password = password; + info.password.password_len = password_len; + return info; +} + +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) +{ + ASSERT(!BAddr_IsInvalid(&server_addr)) + ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6) +#ifndef NDEBUG + for (size_t i = 0; i < num_auth_info; i++) { + ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED || + auth_info[i].auth_type == SOCKS_METHOD_USERNAME_PASSWORD) + } +#endif + + // init arguments + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->dest_addr = dest_addr; + o->handler = handler; + o->user = user; + o->reactor = reactor; + + // set no buffer + o->buffer = NULL; + + // init connector + if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) { + BLog(BLOG_ERROR, "BConnector_Init failed"); + goto fail0; + } + + // set state + o->state = STATE_CONNECTING; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void BSocksClient_Free (BSocksClient *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + if (o->state != STATE_CONNECTING) { + if (o->state == STATE_UP) { + // free up I/O + free_up_io(o); + } else { + // free control I/O + free_control_io(o); + } + + // free connection + BConnection_Free(&o->con); + } + + // free connector + BConnector_Free(&o->connector); + + // free buffer + if (o->buffer) { + BFree(o->buffer); + } +} + +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_SendAsync_GetIf(&o->con); +} + +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return BConnection_RecvAsync_GetIf(&o->con); +} diff --git a/external/badvpn_dns/socksclient/BSocksClient.h b/external/badvpn_dns/socksclient/BSocksClient.h new file mode 100644 index 00000000..f19b3a85 --- /dev/null +++ b/external/badvpn_dns/socksclient/BSocksClient.h @@ -0,0 +1,147 @@ +/** + * @file BSocksClient.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * SOCKS5 client. TCP only, no authentication. + */ + +#ifndef BADVPN_SOCKS_BSOCKSCLIENT_H +#define BADVPN_SOCKS_BSOCKSCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BSOCKSCLIENT_EVENT_ERROR 1 +#define BSOCKSCLIENT_EVENT_UP 2 +#define BSOCKSCLIENT_EVENT_ERROR_CLOSED 3 + +/** + * Handler for events generated by the SOCKS client. + * + * @param user as in {@link BSocksClient_Init} + * @param event event type. One of BSOCKSCLIENT_EVENT_ERROR, BSOCKSCLIENT_EVENT_UP + * and BSOCKSCLIENT_EVENT_ERROR_CLOSED. + * If event is BSOCKSCLIENT_EVENT_UP, the object was previously in down + * state and has transitioned to up state; I/O can be done from this point on. + * If event is BSOCKSCLIENT_EVENT_ERROR or BSOCKSCLIENT_EVENT_ERROR_CLOSED, + * the object must be freed from within the job closure of this handler, + * and no further I/O must be attempted. + */ +typedef void (*BSocksClient_handler) (void *user, int event); + +struct BSocksClient_auth_info { + int auth_type; + union { + struct { + const char *username; + size_t username_len; + const char *password; + size_t password_len; + } password; + }; +}; + +typedef struct { + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr dest_addr; + BSocksClient_handler handler; + void *user; + BReactor *reactor; + int state; + char *buffer; + BConnector connector; + BConnection con; + union { + struct { + PacketPassInterface *send_if; + PacketStreamSender send_sender; + StreamRecvInterface *recv_if; + uint8_t *recv_dest; + int recv_len; + int recv_total; + } control; + }; + DebugError d_err; + DebugObject d_obj; +} BSocksClient; + +struct BSocksClient_auth_info BSocksClient_auth_none (void); +struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, size_t username_len, const char *password, size_t password_len); + +/** + * Initializes the object. + * The object is initialized in down state. The object must transition to up + * state before the user may begin any I/O. + * + * @param o the object + * @param server_addr SOCKS5 server address + * @param dest_addr remote address + * @param handler handler for up and error events + * @param user value passed to handler + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int BSocksClient_Init (BSocksClient *o, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + */ +void BSocksClient_Free (BSocksClient *o); + +/** + * Returns the send interface. + * The object must be in up state. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o); + +/** + * Returns the receive interface. + * The object must be in up state. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o); + +#endif diff --git a/external/badvpn_dns/socksclient/CMakeLists.txt b/external/badvpn_dns/socksclient/CMakeLists.txt new file mode 100644 index 00000000..7b88d8ea --- /dev/null +++ b/external/badvpn_dns/socksclient/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(socksclient "system;flow;flowextra" "" BSocksClient.c) diff --git a/external/badvpn_dns/stringmap/BStringMap.c b/external/badvpn_dns/stringmap/BStringMap.c new file mode 100644 index 00000000..d53f10ee --- /dev/null +++ b/external/badvpn_dns/stringmap/BStringMap.c @@ -0,0 +1,198 @@ +/** + * @file BStringMap.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +static int string_comparator (void *unused, char **str1, char **str2) +{ + int c = strcmp(*str1, *str2); + return B_COMPARE(c, 0); +} + +static void free_entry (BStringMap *o, struct BStringMap_entry *e) +{ + BAVL_Remove(&o->tree, &e->tree_node); + free(e->value); + free(e->key); + free(e); +} + +void BStringMap_Init (BStringMap *o) +{ + // init tree + BAVL_Init(&o->tree, OFFSET_DIFF(struct BStringMap_entry, key, tree_node), (BAVL_comparator)string_comparator, NULL); + + DebugObject_Init(&o->d_obj); +} + +int BStringMap_InitCopy (BStringMap *o, const BStringMap *src) +{ + BStringMap_Init(o); + + const char *key = BStringMap_First(src); + while (key) { + if (!BStringMap_Set(o, key, BStringMap_Get(src, key))) { + goto fail1; + } + key = BStringMap_Next(src, key); + } + + return 1; + +fail1: + BStringMap_Free(o); + return 0; +} + +void BStringMap_Free (BStringMap *o) +{ + DebugObject_Free(&o->d_obj); + + // free entries + BAVLNode *tree_node; + while (tree_node = BAVL_GetFirst(&o->tree)) { + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + free_entry(o, e); + } +} + +const char * BStringMap_Get (const BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + + // lookup + BAVLNode *tree_node = BAVL_LookupExact(&o->tree, &key); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return e->value; +} + +int BStringMap_Set (BStringMap *o, const char *key, const char *value) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + ASSERT(value) + + // alloc entry + struct BStringMap_entry *e = malloc(sizeof(*e)); + if (!e) { + goto fail0; + } + + // alloc and set key + if (!(e->key = malloc(strlen(key) + 1))) { + goto fail1; + } + strcpy(e->key, key); + + // alloc and set value + if (!(e->value = malloc(strlen(value) + 1))) { + goto fail2; + } + strcpy(e->value, value); + + // try inserting to tree + BAVLNode *ex_tree_node; + if (!BAVL_Insert(&o->tree, &e->tree_node, &ex_tree_node)) { + // remove existing entry + struct BStringMap_entry *ex_e = UPPER_OBJECT(ex_tree_node, struct BStringMap_entry, tree_node); + free_entry(o, ex_e); + + // insert new node + ASSERT_EXECUTE(BAVL_Insert(&o->tree, &e->tree_node, NULL)) + } + + return 1; + +fail2: + free(e->key); +fail1: + free(e); +fail0: + return 0; +} + +void BStringMap_Unset (BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + + // lookup + BAVLNode *tree_node = BAVL_LookupExact(&o->tree, &key); + if (!tree_node) { + return; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + // remove + free_entry(o, e); +} + +const char * BStringMap_First (const BStringMap *o) +{ + DebugObject_Access(&o->d_obj); + + // get first + BAVLNode *tree_node = BAVL_GetFirst(&o->tree); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return e->key; +} + +const char * BStringMap_Next (const BStringMap *o, const char *key) +{ + DebugObject_Access(&o->d_obj); + ASSERT(key) + ASSERT(BAVL_LookupExact(&o->tree, &key)) + + // get entry + struct BStringMap_entry *e = UPPER_OBJECT(BAVL_LookupExact(&o->tree, &key), struct BStringMap_entry, tree_node); + + // get next + BAVLNode *tree_node = BAVL_GetNext(&o->tree, &e->tree_node); + if (!tree_node) { + return NULL; + } + struct BStringMap_entry *next_e = UPPER_OBJECT(tree_node, struct BStringMap_entry, tree_node); + + return next_e->key; +} diff --git a/external/badvpn_dns/stringmap/BStringMap.h b/external/badvpn_dns/stringmap/BStringMap.h new file mode 100644 index 00000000..39b20715 --- /dev/null +++ b/external/badvpn_dns/stringmap/BStringMap.h @@ -0,0 +1,57 @@ +/** + * @file BStringMap.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_STRINGMAP_BSTRINGMAP_H +#define BADVPN_STRINGMAP_BSTRINGMAP_H + +#include +#include +#include + +struct BStringMap_entry { + char *key; + char *value; + BAVLNode tree_node; +}; + +typedef struct { + BAVL tree; + DebugObject d_obj; +} BStringMap; + +void BStringMap_Init (BStringMap *o); +int BStringMap_InitCopy (BStringMap *o, const BStringMap *src) WARN_UNUSED; +void BStringMap_Free (BStringMap *o); +const char * BStringMap_Get (const BStringMap *o, const char *key); +int BStringMap_Set (BStringMap *o, const char *key, const char *value) WARN_UNUSED; +void BStringMap_Unset (BStringMap *o, const char *key); +const char * BStringMap_First (const BStringMap *o); +const char * BStringMap_Next (const BStringMap *o, const char *key); + +#endif diff --git a/external/badvpn_dns/stringmap/CMakeLists.txt b/external/badvpn_dns/stringmap/CMakeLists.txt new file mode 100644 index 00000000..74dd8a10 --- /dev/null +++ b/external/badvpn_dns/stringmap/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(stringmap "" "" BStringMap.c) diff --git a/external/badvpn_dns/structure/BAVL.h b/external/badvpn_dns/structure/BAVL.h new file mode 100644 index 00000000..66993b32 --- /dev/null +++ b/external/badvpn_dns/structure/BAVL.h @@ -0,0 +1,797 @@ +/** + * @file BAVL.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * AVL tree. + */ + +#ifndef BADVPN_STRUCTURE_BAVL_H +#define BADVPN_STRUCTURE_BAVL_H + +//#define BAVL_DEBUG + +#include +#include + +#include + +/** + * Handler function called by tree algorithms to compare two values. + * For any two values, the comparator must always return the same result. + * The <= relation defined by the comparator must be a total order. + * Values are obtained like that: + * - The value of a node in the tree, or a node that is being inserted is: + * (uint8_t *)node + offset. + * - The value being looked up is the same as given to the lookup function. + * + * @param user as in {@link BAVL_Init} + * @param val1 first value + * @param val2 second value + * @return -1 if val1 < val2, 0 if val1 = val2, 1 if val1 > val2 + */ +typedef int (*BAVL_comparator) (void *user, void *val1, void *val2); + +struct BAVLNode; + +/** + * AVL tree. + */ +typedef struct { + int offset; + BAVL_comparator comparator; + void *user; + struct BAVLNode *root; +} BAVL; + +/** + * AVL tree node. + */ +typedef struct BAVLNode { + struct BAVLNode *parent; + struct BAVLNode *link[2]; + int8_t balance; +#ifdef BAVL_COUNT + uint64_t count; +#endif +} BAVLNode; + +/** + * Initializes the tree. + * + * @param o tree to initialize + * @param offset offset of a value from its node + * @param comparator value comparator handler function + * @param user value to pass to comparator + */ +static void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user); + +/** + * Inserts a node into the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node uninitialized node to insert. Must have a valid value (its value + * may be passed to the comparator during insertion). + * @param ref if not NULL, will return (regardless if insertion succeeded): + * - the greatest node lesser than the inserted value, or (not in order) + * - the smallest node greater than the inserted value, or + * - NULL meaning there were no nodes in the tree. + * @param 1 on success, 0 if an element with an equal value is already in the tree + */ +static int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref); + +/** + * Removes a node from the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param node node to remove + */ +static void BAVL_Remove (BAVL *o, BAVLNode *node); + +/** + * Checks if the tree is empty. + * Must not be called from comparator. + * + * @param o the tree + * @return 1 if empty, 0 if not + */ +static int BAVL_IsEmpty (const BAVL *o); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else if the tree is not empty: + * - the greatest node lesser than the given value, or (not in order) + * - the smallest node greater than the given value. + * NULL if the tree is empty. + */ +static BAVLNode * BAVL_Lookup (const BAVL *o, void *val); + +/** + * Looks for a value in the tree. + * Must not be called from comparator. + * + * @param o the tree + * @param val value to look for + * @return If a node is in the thee with an equal value, that node. + * Else NULL. + */ +static BAVLNode * BAVL_LookupExact (const BAVL *o, void *val); + +/** + * Returns the smallest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return smallest node or NULL + */ +static BAVLNode * BAVL_GetFirst (const BAVL *o); + +/** + * Returns the greatest node in the tree, or NULL if the tree is empty. + * + * @param o the tree + * @return greatest node or NULL + */ +static BAVLNode * BAVL_GetLast (const BAVL *o); + +/** + * Returns the node that follows the given node, or NULL if it's the + * last node. + * + * @param o the tree + * @param n node + * @return next node, or NULL + */ +static BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n); + +/** + * Returns the node that precedes the given node, or NULL if it's the + * first node. + * + * @param o the tree + * @param n node + * @return previous node, or NULL + */ +static BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n); + +#ifdef BAVL_COUNT +static uint64_t BAVL_Count (const BAVL *o); +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n); +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index); +#endif + +static void BAVL_Verify (BAVL *o); + +#define BAVL_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define BAVL_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) + +static void * _BAVL_node_value (const BAVL *o, BAVLNode *n) +{ + return ((uint8_t *)n + o->offset); +} + +static int _BAVL_compare_values (const BAVL *o, void *v1, void *v2) +{ + int res = o->comparator(o->user, v1, v2); + + ASSERT(res == -1 || res == 0 || res == 1) + + return res; +} + +static int _BAVL_compare_nodes (BAVL *o, BAVLNode *n1, BAVLNode *n2) +{ + return _BAVL_compare_values(o, _BAVL_node_value(o, n1), _BAVL_node_value(o, n2)); +} + +#ifdef BAVL_AUTO_VERIFY +#define BAVL_ASSERT(_h) BAVL_Verify((_h)); +#else +#define BAVL_ASSERT(_h) +#endif + +static int _BAVL_assert_recurser (BAVL *o, BAVLNode *n) +{ + ASSERT_FORCE(n->balance >= -1) + ASSERT_FORCE(n->balance <= 1) + + int height_left = 0; + int height_right = 0; +#ifdef BAVL_COUNT + uint64_t count_left = 0; + uint64_t count_right = 0; +#endif + + // check left subtree + if (n->link[0]) { + // check parent link + ASSERT_FORCE(n->link[0]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[0], n) == -1) + // recursively calculate height + height_left = _BAVL_assert_recurser(o, n->link[0]); +#ifdef BAVL_COUNT + count_left = n->link[0]->count; +#endif + } + + // check right subtree + if (n->link[1]) { + // check parent link + ASSERT_FORCE(n->link[1]->parent == n) + // check binary search tree + ASSERT_FORCE(_BAVL_compare_nodes(o, n->link[1], n) == 1) + // recursively calculate height + height_right = _BAVL_assert_recurser(o, n->link[1]); +#ifdef BAVL_COUNT + count_right = n->link[1]->count; +#endif + } + + // check balance factor + ASSERT_FORCE(n->balance == height_right - height_left) + +#ifdef BAVL_COUNT + // check count + ASSERT_FORCE(n->count == 1 + count_left + count_right) +#endif + + return (BAVL_MAX(height_left, height_right) + 1); +} + +#ifdef BAVL_COUNT +static void _BAVL_update_count_from_children (BAVLNode *n) +{ + n->count = 1 + (n->link[0] ? n->link[0]->count : 0) + (n->link[1] ? n->link[1]->count : 0); +} +#endif + +static void _BAVL_rotate (BAVL *tree, BAVLNode *r, uint8_t dir) +{ + BAVLNode *nr = r->link[!dir]; + + r->link[!dir] = nr->link[dir]; + if (r->link[!dir]) { + r->link[!dir]->parent = r; + } + nr->link[dir] = r; + nr->parent = r->parent; + if (nr->parent) { + nr->parent->link[r == r->parent->link[1]] = nr; + } else { + tree->root = nr; + } + r->parent = nr; + +#ifdef BAVL_COUNT + // update counts + _BAVL_update_count_from_children(r); // first r! + _BAVL_update_count_from_children(nr); +#endif +} + +static BAVLNode * _BAVL_subtree_max (BAVLNode *n) +{ + ASSERT(n) + while (n->link[1]) { + n = n->link[1]; + } + return n; +} + +static void _BAVL_replace_subtree (BAVL *tree, BAVLNode *dest, BAVLNode *n) +{ + ASSERT(dest) + + if (dest->parent) { + dest->parent->link[dest == dest->parent->link[1]] = n; + } else { + tree->root = n; + } + if (n) { + n->parent = dest->parent; + } + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *c = dest->parent; c; c = c->parent) { + ASSERT(c->count >= dest->count) + c->count -= dest->count; + if (n) { + ASSERT(n->count <= UINT64_MAX - c->count) + c->count += n->count; + } + } +#endif +} + +static void _BAVL_swap_nodes (BAVL *tree, BAVLNode *n1, BAVLNode *n2) +{ + if (n2->parent == n1 || n1->parent == n2) { + // when the nodes are directly connected we need special handling + // make sure n1 is above n2 + if (n1->parent == n2) { + BAVLNode *t = n1; + n1 = n2; + n2 = t; + } + + uint8_t side = (n2 == n1->link[1]); + BAVLNode *c = n1->link[!side]; + + if (n1->link[0] = n2->link[0]) { + n1->link[0]->parent = n1; + } + if (n1->link[1] = n2->link[1]) { + n1->link[1]->parent = n1; + } + + if (n2->parent = n1->parent) { + n2->parent->link[n1 == n1->parent->link[1]] = n2; + } else { + tree->root = n2; + } + + n2->link[side] = n1; + n1->parent = n2; + if (n2->link[!side] = c) { + c->parent = n2; + } + } else { + BAVLNode *temp; + + // swap parents + temp = n1->parent; + if (n1->parent = n2->parent) { + n1->parent->link[n2 == n2->parent->link[1]] = n1; + } else { + tree->root = n1; + } + if (n2->parent = temp) { + n2->parent->link[n1 == temp->link[1]] = n2; + } else { + tree->root = n2; + } + + // swap left children + temp = n1->link[0]; + if (n1->link[0] = n2->link[0]) { + n1->link[0]->parent = n1; + } + if (n2->link[0] = temp) { + n2->link[0]->parent = n2; + } + + // swap right children + temp = n1->link[1]; + if (n1->link[1] = n2->link[1]) { + n1->link[1]->parent = n1; + } + if (n2->link[1] = temp) { + n2->link[1]->parent = n2; + } + } + + // swap balance factors + int8_t b = n1->balance; + n1->balance = n2->balance; + n2->balance = b; + +#ifdef BAVL_COUNT + // swap counts + uint64_t c = n1->count; + n1->count = n2->count; + n2->count = c; +#endif +} + +static void _BAVL_rebalance (BAVL *o, BAVLNode *node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(node->balance >= -1 && node->balance <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = BAVL_MAX(deltac, BAVL_OPTNEG(node->balance, side)) - BAVL_MAX(0, BAVL_OPTNEG(node->balance, side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + node->balance -= BAVL_OPTNEG(deltac, side); + + BAVLNode *child; + BAVLNode *gchild; + + // perform transformations if the balance factor is wrong + if (node->balance == 2 || node->balance == -2) { + uint8_t bside; + int8_t bsidef; + if (node->balance == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(node->link[bside]) + child = node->link[bside]; + switch (child->balance * bsidef) { + case 1: + _BAVL_rotate(o, node, !bside); + node->balance = 0; + child->balance = 0; + node = child; + delta -= 1; + break; + case 0: + _BAVL_rotate(o, node, !bside); + node->balance = 1 * bsidef; + child->balance = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(child->link[!bside]) + gchild = child->link[!bside]; + _BAVL_rotate(o, child, bside); + _BAVL_rotate(o, node, !bside); + node->balance = -BAVL_MAX(0, gchild->balance * bsidef) * bsidef; + child->balance = BAVL_MAX(0, -gchild->balance * bsidef) * bsidef; + gchild->balance = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (node->parent) { + _BAVL_rebalance(o, node->parent, node == node->parent->link[1], delta); + } +} + +void BAVL_Init (BAVL *o, int offset, BAVL_comparator comparator, void *user) +{ + o->offset = offset; + o->comparator = comparator; + o->user = user; + o->root = NULL; + + BAVL_ASSERT(o) +} + +int BAVL_Insert (BAVL *o, BAVLNode *node, BAVLNode **ref) +{ + // insert to root? + if (!o->root) { + o->root = node; + node->parent = NULL; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + + BAVL_ASSERT(o) + + if (ref) { + *ref = NULL; + } + return 1; + } + + // find node to insert to + BAVLNode *c = o->root; + int side; + while (1) { + // compare + int comp = _BAVL_compare_nodes(o, node, c); + + // have we found a node that compares equal? + if (comp == 0) { + if (ref) { + *ref = c; + } + return 0; + } + + side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + break; + } + + c = c->link[side]; + } + + // insert + c->link[side] = node; + node->parent = c; + node->link[0] = NULL; + node->link[1] = NULL; + node->balance = 0; +#ifdef BAVL_COUNT + node->count = 1; +#endif + +#ifdef BAVL_COUNT + // update counts + for (BAVLNode *p = c; p; p = p->parent) { + ASSERT(p->count < UINT64_MAX) + p->count++; + } +#endif + + // rebalance + _BAVL_rebalance(o, c, side, 1); + + BAVL_ASSERT(o) + + if (ref) { + *ref = c; + } + return 1; +} + +void BAVL_Remove (BAVL *o, BAVLNode *node) +{ + // if we have both subtrees, swap the node and the largest node + // in the left subtree, so we have at most one subtree + if (node->link[0] && node->link[1]) { + // find the largest node in the left subtree + BAVLNode *max = _BAVL_subtree_max(node->link[0]); + // swap the nodes + _BAVL_swap_nodes(o, node, max); + } + + // have at most one child now + ASSERT(!(node->link[0] && node->link[1])) + + BAVLNode *parent = node->parent; + BAVLNode *child = (node->link[0] ? node->link[0] : node->link[1]); + + if (parent) { + // remember on which side node is + int side = (node == parent->link[1]); + // replace node with child + _BAVL_replace_subtree(o, node, child); + // rebalance + _BAVL_rebalance(o, parent, side, -1); + } else { + // replace node with child + _BAVL_replace_subtree(o, node, child); + } + + BAVL_ASSERT(o) +} + +int BAVL_IsEmpty (const BAVL *o) +{ + return (!o->root); +} + +BAVLNode * BAVL_Lookup (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return c; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_LookupExact (const BAVL *o, void *val) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *c = o->root; + while (1) { + // compare + int comp = _BAVL_compare_values(o, val, _BAVL_node_value(o, c)); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (!c->link[side]) { + return NULL; + } + + c = c->link[side]; + } +} + +BAVLNode * BAVL_GetFirst (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[0]) { + n = n->link[0]; + } + + return n; +} + +BAVLNode * BAVL_GetLast (const BAVL *o) +{ + if (!o->root) { + return NULL; + } + + BAVLNode *n = o->root; + while (n->link[1]) { + n = n->link[1]; + } + + return n; +} + +BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n) +{ + if (n->link[1]) { + n = n->link[1]; + while (n->link[0]) { + n = n->link[0]; + } + } else { + while (n->parent && n == n->parent->link[1]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +BAVLNode * BAVL_GetPrev (const BAVL *o, BAVLNode *n) +{ + if (n->link[0]) { + n = n->link[0]; + while (n->link[1]) { + n = n->link[1]; + } + } else { + while (n->parent && n == n->parent->link[0]) { + n = n->parent; + } + n = n->parent; + } + + return n; +} + +#ifdef BAVL_COUNT + +static uint64_t BAVL_Count (const BAVL *o) +{ + return (o->root ? o->root->count : 0); +} + +static uint64_t BAVL_IndexOf (const BAVL *o, BAVLNode *n) +{ + uint64_t index = (n->link[0] ? n->link[0]->count : 0); + + for (BAVLNode *c = n; c->parent; c = c->parent) { + if (c == c->parent->link[1]) { + ASSERT(c->parent->count > c->count) + ASSERT(c->parent->count - c->count <= UINT64_MAX - index) + index += c->parent->count - c->count; + } + } + + return index; +} + +static BAVLNode * BAVL_GetAt (const BAVL *o, uint64_t index) +{ + if (index >= BAVL_Count(o)) { + return NULL; + } + + BAVLNode *c = o->root; + + while (1) { + ASSERT(c) + ASSERT(index < c->count) + + uint64_t left_count = (c->link[0] ? c->link[0]->count : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = c->link[0]; + } else { + c = c->link[1]; + index -= left_count + 1; + } + } +} + +#endif + +static void BAVL_Verify (BAVL *o) +{ + if (o->root) { + ASSERT(!o->root->parent) + _BAVL_assert_recurser(o, o->root); + } +} + +#endif diff --git a/external/badvpn_dns/structure/CAvl.h b/external/badvpn_dns/structure/CAvl.h new file mode 100644 index 00000000..ea33a3ee --- /dev/null +++ b/external/badvpn_dns/structure/CAvl.h @@ -0,0 +1,36 @@ +/** + * @file CAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CAVL_H +#define BADVPN_CAVL_H + +#include +#include + +#endif diff --git a/external/badvpn_dns/structure/CAvl_decl.h b/external/badvpn_dns/structure/CAvl_decl.h new file mode 100644 index 00000000..7d54a815 --- /dev/null +++ b/external/badvpn_dns/structure/CAvl_decl.h @@ -0,0 +1,77 @@ +/** + * @file CAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +typedef struct { + CAvlLink root; +} CAvl; + +typedef struct { + CAvlEntry *ptr; + CAvlLink link; +} CAvlRef; + +static int CAvlIsNullRef (CAvlRef node); +static int CAvlIsValidRef (CAvlRef node); +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link); + +static void CAvl_Init (CAvl *o); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref); +#else +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index); +#endif +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node); +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key); +#endif +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg); +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node); +static int CAvl_IsEmpty (const CAvl *o); +static void CAvl_Verify (const CAvl *o, CAvlArg arg); +#if CAVL_PARAM_FEATURE_COUNTS +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg); +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index); +#endif +#if CAVL_PARAM_FEATURE_ASSOC +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg); +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node); +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user); +#endif + +#include "CAvl_footer.h" diff --git a/external/badvpn_dns/structure/CAvl_footer.h b/external/badvpn_dns/structure/CAvl_footer.h new file mode 100644 index 00000000..43b85c3a --- /dev/null +++ b/external/badvpn_dns/structure/CAvl_footer.h @@ -0,0 +1,113 @@ +/** + * @file CAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef CAVL_PARAM_NAME +#undef CAVL_PARAM_FEATURE_COUNTS +#undef CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +#undef CAVL_PARAM_FEATURE_NOKEYS +#undef CAVL_PARAM_FEATURE_ASSOC +#undef CAVL_PARAM_TYPE_ENTRY +#undef CAVL_PARAM_TYPE_LINK +#undef CAVL_PARAM_TYPE_KEY +#undef CAVL_PARAM_TYPE_ARG +#undef CAVL_PARAM_TYPE_COUNT +#undef CAVL_PARAM_TYPE_ASSOC +#undef CAVL_PARAM_VALUE_COUNT_MAX +#undef CAVL_PARAM_VALUE_NULL +#undef CAVL_PARAM_VALUE_ASSOC_ZERO +#undef CAVL_PARAM_FUN_DEREF +#undef CAVL_PARAM_FUN_COMPARE_ENTRIES +#undef CAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef CAVL_PARAM_FUN_ASSOC_VALUE +#undef CAVL_PARAM_FUN_ASSOC_OPER +#undef CAVL_PARAM_MEMBER_CHILD +#undef CAVL_PARAM_MEMBER_BALANCE +#undef CAVL_PARAM_MEMBER_PARENT +#undef CAVL_PARAM_MEMBER_COUNT +#undef CAVL_PARAM_MEMBER_ASSOC + +#undef CAvl +#undef CAvlEntry +#undef CAvlLink +#undef CAvlRef +#undef CAvlArg +#undef CAvlKey +#undef CAvlCount +#undef CAvlAssoc + +#undef CAvlIsNullRef +#undef CAvlIsValidRef +#undef CAvlDeref + +#undef CAvl_Init +#undef CAvl_Insert +#undef CAvl_InsertAt +#undef CAvl_Remove +#undef CAvl_Lookup +#undef CAvl_LookupExact +#undef CAvl_GetFirstGreater +#undef CAvl_GetLastLesser +#undef CAvl_GetFirstGreaterEqual +#undef CAvl_GetLastLesserEqual +#undef CAvl_GetFirst +#undef CAvl_GetLast +#undef CAvl_GetNext +#undef CAvl_GetPrev +#undef CAvl_IsEmpty +#undef CAvl_Verify +#undef CAvl_Count +#undef CAvl_IndexOf +#undef CAvl_GetAt +#undef CAvl_AssocSum +#undef CAvl_ExclusiveAssocPrefixSum +#undef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual + +#undef CAvl_link +#undef CAvl_balance +#undef CAvl_parent +#undef CAvl_count +#undef CAvl_assoc +#undef CAvl_nulllink +#undef CAvl_nullref +#undef CAvl_compare_entries +#undef CAvl_compare_key_entry +#undef CAvl_compute_node_assoc +#undef CAvl_check_parent +#undef CAvl_verify_recurser +#undef CAvl_assert_tree +#undef CAvl_update_count_from_children +#undef CAvl_rotate +#undef CAvl_subtree_min +#undef CAvl_subtree_max +#undef CAvl_replace_subtree_fix_assoc +#undef CAvl_swap_for_remove +#undef CAvl_rebalance +#undef CAvl_child_count +#undef CAvl_MAX +#undef CAvl_OPTNEG diff --git a/external/badvpn_dns/structure/CAvl_header.h b/external/badvpn_dns/structure/CAvl_header.h new file mode 100644 index 00000000..91ea7df6 --- /dev/null +++ b/external/badvpn_dns/structure/CAvl_header.h @@ -0,0 +1,141 @@ +/** + * @file CAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// CAVL_PARAM_NAME - name of this data structure +// CAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// CAVL_PARAM_FEATURE_KEYS_ARE_INDICES - (0 or 1) whether to assume the keys are entry indices +// (number of entries lesser than given entry). If yes, CAVL_PARAM_TYPE_KEY is unused. +// Requires CAVL_PARAM_FEATURE_COUNTS. +// CAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// CAVL_PARAM_FEATURE_ASSOC - define to 1 for computation of an associative operation on subtrees. +// If enabled, the following macros must be defined: CAVL_PARAM_TYPE_ASSOC, +// CAVL_PARAM_VALUE_ASSOC_ZERO, CAVL_PARAM_FUN_ASSOC_VALUE, +// CAVL_PARAM_FUN_ASSOC_OPER, CAVL_PARAM_MEMBER_ASSOC. +// CAVL_PARAM_TYPE_ENTRY - type of entry +// CAVL_PARAM_TYPE_LINK - type of entry link (usually pointer or index) +// CAVL_PARAM_TYPE_KEY - type of key (only if not CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and +// not CAVL_PARAM_FEATURE_NOKEYS) +// CAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// CAVL_PARAM_TYPE_COUNT - type of count (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_TYPE_ASSOC - type of associative operation result +// CAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (type is CAVL_PARAM_TYPE_COUNT) +// CAVL_PARAM_VALUE_NULL - value of invalid link (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_VALUE_ASSOC_ZERO - zero value for associative operation (type is CAVL_PARAM_TYPE_ASSOC). +// This must be both a left- and right-identity for the associative operation. +// CAVL_PARAM_FUN_DEREF(arg, link) - dereference a non-null link; returns pointer to CAVL_PARAM_TYPE_LINK +// CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare to entries; returns -1/0/1 +// CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 +// CAVL_PARAM_FUN_ASSOC_VALUE(arg, entry) - get value of a node for associative operation. +// The result will be cast to CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_FUN_ASSOC_OPER(arg, value1, value2) - compute the associative operation on two values. +// The type of the two values is CAVL_PARAM_TYPE_ASSOC, and the result will be cast to +// CAVL_PARAM_TYPE_ASSOC. +// CAVL_PARAM_MEMBER_CHILD - name of the child member in entry (type is CAVL_PARAM_TYPE_LINK[2]) +// CAVL_PARAM_MEMBER_BALANCE - name of the balance member in entry (type is any signed integer) +// CAVL_PARAM_MEMBER_PARENT - name of the parent member in entry (type is CAVL_PARAM_TYPE_LINK) +// CAVL_PARAM_MEMBER_COUNT - name of the count member in entry (type is CAVL_PARAM_TYPE_COUNT) +// (only if CAVL_PARAM_FEATURE_COUNTS) +// CAVL_PARAM_MEMBER_ASSOC - name of assoc member in entry (type is CAVL_PARAM_TYPE_ASSOC) + +#ifndef BADVPN_CAVL_H +#error CAvl.h has not been included +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_COUNTS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES requires CAVL_PARAM_FEATURE_COUNTS +#endif + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && CAVL_PARAM_FEATURE_NOKEYS +#error CAVL_PARAM_FEATURE_KEYS_ARE_INDICES and CAVL_PARAM_FEATURE_NOKEYS cannot be used together +#endif + +// types +#define CAvl CAVL_PARAM_NAME +#define CAvlEntry CAVL_PARAM_TYPE_ENTRY +#define CAvlLink CAVL_PARAM_TYPE_LINK +#define CAvlRef MERGE(CAVL_PARAM_NAME, Ref) +#define CAvlArg CAVL_PARAM_TYPE_ARG +#define CAvlKey CAVL_PARAM_TYPE_KEY +#define CAvlCount CAVL_PARAM_TYPE_COUNT +#define CAvlAssoc CAVL_PARAM_TYPE_ASSOC + +// non-object public functions +#define CAvlIsNullRef MERGE(CAvl, IsNullRef) +#define CAvlIsValidRef MERGE(CAvl, IsValidRef) +#define CAvlDeref MERGE(CAvl, Deref) + +// public functions +#define CAvl_Init MERGE(CAvl, _Init) +#define CAvl_Insert MERGE(CAvl, _Insert) +#define CAvl_InsertAt MERGE(CAvl, _InsertAt) +#define CAvl_Remove MERGE(CAvl, _Remove) +#define CAvl_Lookup MERGE(CAvl, _Lookup) +#define CAvl_LookupExact MERGE(CAvl, _LookupExact) +#define CAvl_GetFirstGreater MERGE(CAvl, _GetFirstGreater) +#define CAvl_GetLastLesser MERGE(CAvl, _GetLastLesser) +#define CAvl_GetFirstGreaterEqual MERGE(CAvl, _GetFirstGreaterEqual) +#define CAvl_GetLastLesserEqual MERGE(CAvl, _GetLastLesserEqual) +#define CAvl_GetFirst MERGE(CAvl, _GetFirst) +#define CAvl_GetLast MERGE(CAvl, _GetLast) +#define CAvl_GetNext MERGE(CAvl, _GetNext) +#define CAvl_GetPrev MERGE(CAvl, _GetPrev) +#define CAvl_IsEmpty MERGE(CAvl, _IsEmpty) +#define CAvl_Verify MERGE(CAvl, _Verify) +#define CAvl_Count MERGE(CAvl, _Count) +#define CAvl_IndexOf MERGE(CAvl, _IndexOf) +#define CAvl_GetAt MERGE(CAvl, _GetAt) +#define CAvl_AssocSum MERGE(CAvl, _AssocSum) +#define CAvl_ExclusiveAssocPrefixSum MERGE(CAvl, _ExclusiveAssocPrefixSum) +#define CAvl_FindLastExclusiveAssocPrefixSumLesserEqual MERGE(CAvl, _FindLastExclusiveAssocPrefixSumLesserEqual) + +// private stuff +#define CAvl_link(entry) ((entry).ptr->CAVL_PARAM_MEMBER_CHILD) +#define CAvl_balance(entry) ((entry).ptr->CAVL_PARAM_MEMBER_BALANCE) +#define CAvl_parent(entry) ((entry).ptr->CAVL_PARAM_MEMBER_PARENT) +#define CAvl_count(entry) ((entry).ptr->CAVL_PARAM_MEMBER_COUNT) +#define CAvl_assoc(entry) ((entry).ptr->CAVL_PARAM_MEMBER_ASSOC) +#define CAvl_nulllink MERGE(CAvl, __nulllink) +#define CAvl_nullref MERGE(CAvl, __nullref) +#define CAvl_compare_entries MERGE(CAVL_PARAM_NAME, _compare_entries) +#define CAvl_compare_key_entry MERGE(CAVL_PARAM_NAME, _compare_key_entry) +#define CAvl_compute_node_assoc MERGE(CAVL_PARAM_NAME, _compute_node_assoc) +#define CAvl_check_parent MERGE(CAVL_PARAM_NAME, _check_parent) +#define CAvl_verify_recurser MERGE(CAVL_PARAM_NAME, _verify_recurser) +#define CAvl_assert_tree MERGE(CAVL_PARAM_NAME, _assert_tree) +#define CAvl_update_count_from_children MERGE(CAVL_PARAM_NAME, _update_count_from_children) +#define CAvl_rotate MERGE(CAVL_PARAM_NAME, _rotate) +#define CAvl_subtree_min MERGE(CAVL_PARAM_NAME, _subtree_min) +#define CAvl_subtree_max MERGE(CAVL_PARAM_NAME, _subtree_max) +#define CAvl_replace_subtree_fix_assoc MERGE(CAVL_PARAM_NAME, _replace_subtree_fix_counts) +#define CAvl_swap_for_remove MERGE(CAVL_PARAM_NAME, _swap_entries) +#define CAvl_rebalance MERGE(CAVL_PARAM_NAME, _rebalance) +#define CAvl_child_count MERGE(CAvl, __child_count) +#define CAvl_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) +#define CAvl_OPTNEG(_a, _neg) ((_neg) ? -(_a) : (_a)) diff --git a/external/badvpn_dns/structure/CAvl_impl.h b/external/badvpn_dns/structure/CAvl_impl.h new file mode 100644 index 00000000..984bdea1 --- /dev/null +++ b/external/badvpn_dns/structure/CAvl_impl.h @@ -0,0 +1,949 @@ +/** + * @file CAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CAvl_header.h" + +static CAvlLink CAvl_nulllink (void) +{ + return CAVL_PARAM_VALUE_NULL; +} + +static CAvlRef CAvl_nullref (void) +{ + CAvlRef n; + n.link = CAVL_PARAM_VALUE_NULL; + n.ptr = NULL; + return n; +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_compare_entries (CAvlArg arg, CAvlRef node1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, node1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#if !CAVL_PARAM_FEATURE_NOKEYS + +static int CAvl_compare_key_entry (CAvlArg arg, CAvlKey key1, CAvlRef node2) +{ + int res = CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, node2); + ASSERT(res >= -1) + ASSERT(res <= 1) + + return res; +} + +#endif + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_compute_node_assoc (CAvlArg arg, CAvlRef node) +{ + CAvlAssoc sum = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); + if (CAvl_link(node)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])), sum); + } + if (CAvl_link(node)[1] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum, CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[1]))); + } + return sum; +} + +#endif + +static int CAvl_check_parent (CAvlRef p, CAvlRef c) +{ + return (p.link == CAvl_parent(c)) && (p.link == CAvl_nulllink() || c.link == CAvl_link(p)[0] || c.link == CAvl_link(p)[1]); +} + +static int CAvl_verify_recurser (CAvlArg arg, CAvlRef n) +{ + ASSERT_FORCE(CAvl_balance(n) >= -1) + ASSERT_FORCE(CAvl_balance(n) <= 1) + + int height_left = 0; + int height_right = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvlCount count_left = 0; + CAvlCount count_right = 0; +#endif + + // check left subtree + if (CAvl_link(n)[0] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[0])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[0]), n) == -1) +#endif + // recursively calculate height + height_left = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[0])); +#if CAVL_PARAM_FEATURE_COUNTS + count_left = CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])); +#endif + } + + // check right subtree + if (CAvl_link(n)[1] != CAvl_nulllink()) { + // check parent link + ASSERT_FORCE(CAvl_parent(CAvlDeref(arg, CAvl_link(n)[1])) == n.link) + // check binary search tree +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + ASSERT_FORCE(CAvl_compare_entries(arg, CAvlDeref(arg, CAvl_link(n)[1]), n) == 1) +#endif + // recursively calculate height + height_right = CAvl_verify_recurser(arg, CAvlDeref(arg, CAvl_link(n)[1])); +#if CAVL_PARAM_FEATURE_COUNTS + count_right = CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])); +#endif + } + + // check balance factor + ASSERT_FORCE(CAvl_balance(n) == height_right - height_left) + +#if CAVL_PARAM_FEATURE_COUNTS + // check count + ASSERT_FORCE(CAvl_count(n) == 1 + count_left + count_right) +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + // check assoc + ASSERT_FORCE(CAvl_assoc(n) == CAvl_compute_node_assoc(arg, n)) +#endif + + return CAvl_MAX(height_left, height_right) + 1; +} + +static void CAvl_assert_tree (CAvl *o, CAvlArg arg) +{ +#ifdef CAVL_AUTO_VERIFY + CAvl_Verify(o, arg); +#endif +} + +#if CAVL_PARAM_FEATURE_COUNTS +static void CAvl_update_count_from_children (CAvlArg arg, CAvlRef n) +{ + CAvlCount left_count = CAvl_link(n)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[0])) : 0; + CAvlCount right_count = CAvl_link(n)[1] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[1])) : 0; + CAvl_count(n) = 1 + left_count + right_count; +} +#endif + +static void CAvl_rotate (CAvl *o, CAvlArg arg, CAvlRef r, uint8_t dir, CAvlRef r_parent) +{ + ASSERT(CAvl_check_parent(r_parent, r)) + CAvlRef nr = CAvlDeref(arg, CAvl_link(r)[!dir]); + + CAvl_link(r)[!dir] = CAvl_link(nr)[dir]; + if (CAvl_link(r)[!dir] != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(r)[!dir])) = r.link; + } + CAvl_link(nr)[dir] = r.link; + CAvl_parent(nr) = r_parent.link; + if (r_parent.link != CAvl_nulllink()) { + CAvl_link(r_parent)[r.link == CAvl_link(r_parent)[1]] = nr.link; + } else { + o->root = nr.link; + } + CAvl_parent(r) = nr.link; + +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_update_count_from_children(arg, r); + CAvl_update_count_from_children(arg, nr); +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(r) = CAvl_compute_node_assoc(arg, r); + CAvl_assoc(nr) = CAvl_compute_node_assoc(arg, nr); +#endif +} + +static CAvlRef CAvl_subtree_min (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[0] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[0]); + } + + return n; +} + +static CAvlRef CAvl_subtree_max (CAvlArg arg, CAvlRef n) +{ + ASSERT(n.link != CAvl_nulllink()) + + while (CAvl_link(n)[1] != CAvl_nulllink()) { + n = CAvlDeref(arg, CAvl_link(n)[1]); + } + + return n; +} + +static void CAvl_replace_subtree_fix_assoc (CAvl *o, CAvlArg arg, CAvlRef dest, CAvlRef n, CAvlRef dest_parent) +{ + ASSERT(dest.link != CAvl_nulllink()) + ASSERT(CAvl_check_parent(dest_parent, dest)) + + if (dest_parent.link != CAvl_nulllink()) { + CAvl_link(dest_parent)[dest.link == CAvl_link(dest_parent)[1]] = n.link; + } else { + o->root = n.link; + } + if (n.link != CAvl_nulllink()) { + CAvl_parent(n) = CAvl_parent(dest); + } + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef c = dest_parent; c.link != CAvl_nulllink(); c = CAvlDeref(arg, CAvl_parent(c))) { +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_count(c) >= CAvl_count(dest)) + CAvl_count(c) -= CAvl_count(dest); + if (n.link != CAvl_nulllink()) { + ASSERT(CAvl_count(n) <= CAVL_PARAM_VALUE_COUNT_MAX - CAvl_count(c)) + CAvl_count(c) += CAvl_count(n); + } +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(c) = CAvl_compute_node_assoc(arg, c); +#endif + } +#endif +} + +static void CAvl_swap_for_remove (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef enode, CAvlRef node_parent, CAvlRef enode_parent) +{ + ASSERT(CAvl_check_parent(node_parent, node)) + ASSERT(CAvl_check_parent(enode_parent, enode)) + + if (enode_parent.link == node.link) { + // when the nodes are directly connected we need special handling + + uint8_t side = (enode.link == CAvl_link(node)[1]); + CAvlRef c = CAvlDeref(arg, CAvl_link(node)[!side]); + + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + + CAvl_parent(enode) = CAvl_parent(node); + if (node_parent.link != CAvl_nulllink()) { + CAvl_link(node_parent)[node.link == CAvl_link(node_parent)[1]] = enode.link; + } else { + o->root = enode.link; + } + + CAvl_link(enode)[side] = node.link; + CAvl_parent(node) = enode.link; + if ((CAvl_link(enode)[!side] = c.link) != CAvl_nulllink()) { + CAvl_parent(c) = enode.link; + } + } else { + CAvlRef temp; + + // swap parents + temp = node_parent; + CAvl_parent(node) = CAvl_parent(enode); + if (enode_parent.link != CAvl_nulllink()) { + CAvl_link(enode_parent)[enode.link == CAvl_link(enode_parent)[1]] = node.link; + } else { + o->root = node.link; + } + CAvl_parent(enode) = temp.link; + if (temp.link != CAvl_nulllink()) { + CAvl_link(temp)[node.link == CAvl_link(temp)[1]] = enode.link; + } else { + o->root = enode.link; + } + + // swap left children + temp = CAvlDeref(arg, CAvl_link(node)[0]); + if ((CAvl_link(node)[0] = CAvl_link(enode)[0]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[0])) = node.link; + } + if ((CAvl_link(enode)[0] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[0])) = enode.link; + } + + // swap right children + temp = CAvlDeref(arg, CAvl_link(node)[1]); + if ((CAvl_link(node)[1] = CAvl_link(enode)[1]) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(node)[1])) = node.link; + } + if ((CAvl_link(enode)[1] = temp.link) != CAvl_nulllink()) { + CAvl_parent(CAvlDeref(arg, CAvl_link(enode)[1])) = enode.link; + } + } + + // swap balance factors + int8_t b = CAvl_balance(node); + CAvl_balance(node) = CAvl_balance(enode); + CAvl_balance(enode) = b; + +#if CAVL_PARAM_FEATURE_COUNTS + // swap counts + CAvlCount c = CAvl_count(node); + CAvl_count(node) = CAvl_count(enode); + CAvl_count(enode) = c; +#endif + + // not fixing assoc values here because CAvl_replace_subtree_fix_assoc() will do it +} + +static void CAvl_rebalance (CAvl *o, CAvlArg arg, CAvlRef node, uint8_t side, int8_t deltac) +{ + ASSERT(side == 0 || side == 1) + ASSERT(deltac >= -1 && deltac <= 1) + ASSERT(CAvl_balance(node) >= -1 && CAvl_balance(node) <= 1) + + // if no subtree changed its height, no more rebalancing is needed + if (deltac == 0) { + return; + } + + // calculate how much our height changed + int8_t delta = CAvl_MAX(deltac, CAvl_OPTNEG(CAvl_balance(node), side)) - CAvl_MAX(0, CAvl_OPTNEG(CAvl_balance(node), side)); + ASSERT(delta >= -1 && delta <= 1) + + // update our balance factor + CAvl_balance(node) -= CAvl_OPTNEG(deltac, side); + + CAvlRef child; + CAvlRef gchild; + + // perform transformations if the balance factor is wrong + if (CAvl_balance(node) == 2 || CAvl_balance(node) == -2) { + uint8_t bside; + int8_t bsidef; + if (CAvl_balance(node) == 2) { + bside = 1; + bsidef = 1; + } else { + bside = 0; + bsidef = -1; + } + + ASSERT(CAvl_link(node)[bside] != CAvl_nulllink()) + child = CAvlDeref(arg, CAvl_link(node)[bside]); + + switch (CAvl_balance(child) * bsidef) { + case 1: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 0; + CAvl_balance(child) = 0; + node = child; + delta -= 1; + break; + case 0: + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = 1 * bsidef; + CAvl_balance(child) = -1 * bsidef; + node = child; + break; + case -1: + ASSERT(CAvl_link(child)[!bside] != CAvl_nulllink()) + gchild = CAvlDeref(arg, CAvl_link(child)[!bside]); + CAvl_rotate(o, arg, child, bside, node); + CAvl_rotate(o, arg, node, !bside, CAvlDeref(arg, CAvl_parent(node))); + CAvl_balance(node) = -CAvl_MAX(0, CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(child) = CAvl_MAX(0, -CAvl_balance(gchild) * bsidef) * bsidef; + CAvl_balance(gchild) = 0; + node = gchild; + delta -= 1; + break; + default: + ASSERT(0); + } + } + + ASSERT(delta >= -1 && delta <= 1) + // Transformations above preserve this. Proof: + // - if a child subtree gained 1 height and rebalancing was needed, + // it was the heavier subtree. Then delta was was originally 1, because + // the heaviest subtree gained one height. If the transformation reduces + // delta by one, it becomes 0. + // - if a child subtree lost 1 height and rebalancing was needed, it + // was the lighter subtree. Then delta was originally 0, because + // the height of the heaviest subtree was unchanged. If the transformation + // reduces delta by one, it becomes -1. + + if (CAvl_parent(node) != CAvl_nulllink()) { + CAvlRef node_parent = CAvlDeref(arg, CAvl_parent(node)); + CAvl_rebalance(o, arg, node_parent, node.link == CAvl_link(node_parent)[1], delta); + } +} + +#if CAVL_PARAM_FEATURE_KEYS_ARE_INDICES +static CAvlCount CAvl_child_count (CAvlArg arg, CAvlRef n, int dir) +{ + return (CAvl_link(n)[dir] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(n)[dir])) : 0); +} +#endif + +static int CAvlIsNullRef (CAvlRef node) +{ + return node.link == CAvl_nulllink(); +} + +static int CAvlIsValidRef (CAvlRef node) +{ + return node.link != CAvl_nulllink(); +} + +static CAvlRef CAvlDeref (CAvlArg arg, CAvlLink link) +{ + if (link == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef n; + n.ptr = CAVL_PARAM_FUN_DEREF(arg, link); + n.link = link; + + ASSERT(n.ptr) + + return n; +} + +static void CAvl_Init (CAvl *o) +{ + o->root = CAvl_nulllink(); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES + +static int CAvl_Insert (CAvl *o, CAvlArg arg, CAvlRef node, CAvlRef *out_ref) +{ + ASSERT(node.link != CAvl_nulllink()) +#if CAVL_PARAM_FEATURE_COUNTS + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) +#endif + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = CAvl_nullref(); + } + return 1; + } + + CAvlRef c = CAvlDeref(arg, o->root); + int side; + while (1) { + int comp = CAvl_compare_entries(arg, node, c); + + if (comp == 0) { + if (out_ref) { + *out_ref = c; + } + return 0; + } + + side = (comp == 1); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(node) = 1; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + +#if CAVL_PARAM_FEATURE_COUNTS || CAVL_PARAM_FEATURE_ASSOC + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { +#if CAVL_PARAM_FEATURE_COUNTS + CAvl_count(p)++; +#endif +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } +#endif + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + + if (out_ref) { + *out_ref = c; + } + return 1; +} + +#else + +static void CAvl_InsertAt (CAvl *o, CAvlArg arg, CAvlRef node, CAvlCount index) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(index <= CAvl_Count(o, arg)) + ASSERT(CAvl_Count(o, arg) < CAVL_PARAM_VALUE_COUNT_MAX) + + // insert to root? + if (o->root == CAvl_nulllink()) { + o->root = node.link; + CAvl_parent(node) = CAvl_nulllink(); + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + CAvl_assert_tree(o, arg); + return; + } + + CAvlRef c = CAvlDeref(arg, o->root); + CAvlCount c_idx = CAvl_child_count(arg, c, 0); + int side; + while (1) { + side = (index > c_idx); + + if (CAvl_link(c)[side] == CAvl_nulllink()) { + break; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + + if (side == 0) { + c_idx -= 1 + CAvl_child_count(arg, c, 1); + } else { + c_idx += 1 + CAvl_child_count(arg, c, 0); + } + } + + CAvl_link(c)[side] = node.link; + CAvl_parent(node) = c.link; + CAvl_link(node)[0] = CAvl_nulllink(); + CAvl_link(node)[1] = CAvl_nulllink(); + CAvl_balance(node) = 0; + CAvl_count(node) = 1; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(node) = CAVL_PARAM_FUN_ASSOC_VALUE(arg, node); +#endif + + for (CAvlRef p = c; p.link != CAvl_nulllink(); p = CAvlDeref(arg, CAvl_parent(p))) { + CAvl_count(p)++; +#if CAVL_PARAM_FEATURE_ASSOC + CAvl_assoc(p) = CAvl_compute_node_assoc(arg, p); +#endif + } + + CAvl_rebalance(o, arg, c, side, 1); + + CAvl_assert_tree(o, arg); + return; +} + +#endif + +static void CAvl_Remove (CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink() && CAvl_link(node)[1] != CAvl_nulllink()) { + CAvlRef max = CAvl_subtree_max(arg, CAvlDeref(arg, CAvl_link(node)[0])); + CAvl_swap_for_remove(o, arg, node, max, CAvlDeref(arg, CAvl_parent(node)), CAvlDeref(arg, CAvl_parent(max))); + } + + ASSERT(CAvl_link(node)[0] == CAvl_nulllink() || CAvl_link(node)[1] == CAvl_nulllink()) + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + CAvlRef child = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvlDeref(arg, CAvl_link(node)[0]) : CAvlDeref(arg, CAvl_link(node)[1])); + + if (paren.link != CAvl_nulllink()) { + int side = (node.link == CAvl_link(paren)[1]); + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + CAvl_rebalance(o, arg, paren, side, -1); + } else { + CAvl_replace_subtree_fix_assoc(o, arg, node, child, paren); + } + + CAvl_assert_tree(o, arg); +} + +#if !CAVL_PARAM_FEATURE_KEYS_ARE_INDICES && !CAVL_PARAM_FEATURE_NOKEYS + +static CAvlRef CAvl_Lookup (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return c; + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_LookupExact (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + while (1) { + // compare + int comp = CAvl_compare_key_entry(arg, key, c); + + // have we found a node that compares equal? + if (comp == 0) { + return c; + } + + int side = (comp == 1); + + // have we reached a leaf? + if (CAvl_link(c)[side] == CAvl_nulllink()) { + return CAvl_nullref(); + } + + c = CAvlDeref(arg, CAvl_link(c)[side]); + } +} + +static CAvlRef CAvl_GetFirstGreater (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) >= 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) < 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesser (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) <= 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) > 0); + + return c; +} + +static CAvlRef CAvl_GetFirstGreaterEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) > 0) { + c = CAvl_GetNext(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) <= 0); + + return c; +} + +static CAvlRef CAvl_GetLastLesserEqual (const CAvl *o, CAvlArg arg, CAvlKey key) +{ + CAvlRef c = CAvl_Lookup(o, arg, key); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + + if (CAvl_compare_key_entry(arg, key, c) < 0) { + c = CAvl_GetPrev(o, arg, c); + if (CAvlIsNullRef(c)) { + return CAvl_nullref(); + } + } + + ASSERT(CAvl_compare_key_entry(arg, key, c) >= 0); + + return c; +} + +#endif + +static CAvlRef CAvl_GetFirst (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_min(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetLast (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAvl_nullref(); + } + + return CAvl_subtree_max(arg, CAvlDeref(arg, o->root)); +} + +static CAvlRef CAvl_GetNext (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + while (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[1]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static CAvlRef CAvl_GetPrev (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + if (CAvl_link(node)[0] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[0]); + while (CAvl_link(node)[1] != CAvl_nulllink()) { + node = CAvlDeref(arg, CAvl_link(node)[1]); + } + } else { + while (CAvl_parent(node) != CAvl_nulllink() && node.link == CAvl_link(CAvlDeref(arg, CAvl_parent(node)))[0]) { + node = CAvlDeref(arg, CAvl_parent(node)); + } + node = CAvlDeref(arg, CAvl_parent(node)); + } + + return node; +} + +static int CAvl_IsEmpty (const CAvl *o) +{ + return o->root == CAvl_nulllink(); +} + +static void CAvl_Verify (const CAvl *o, CAvlArg arg) +{ + if (o->root != CAvl_nulllink()) { + CAvlRef root = CAvlDeref(arg, o->root); + ASSERT(CAvl_parent(root) == CAvl_nulllink()) + CAvl_verify_recurser(arg, root); + } +} + +#if CAVL_PARAM_FEATURE_COUNTS + +static CAvlCount CAvl_Count (const CAvl *o, CAvlArg arg) +{ + return (o->root != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, o->root)) : 0); +} + +static CAvlCount CAvl_IndexOf (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlCount index = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(node)[0])) : 0); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + ASSERT(CAvl_count(paren) > CAvl_count(c)) + ASSERT(CAvl_count(paren) - CAvl_count(c) <= CAVL_PARAM_VALUE_COUNT_MAX - index) + index += CAvl_count(paren) - CAvl_count(c); + } + } + + return index; +} + +static CAvlRef CAvl_GetAt (const CAvl *o, CAvlArg arg, CAvlCount index) +{ + if (index >= CAvl_Count(o, arg)) { + return CAvl_nullref(); + } + + CAvlRef c = CAvlDeref(arg, o->root); + + while (1) { + ASSERT(c.link != CAvl_nulllink()) + ASSERT(index < CAvl_count(c)) + + CAvlCount left_count = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_count(CAvlDeref(arg, CAvl_link(c)[0])) : 0); + + if (index == left_count) { + return c; + } + + if (index < left_count) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + c = CAvlDeref(arg, CAvl_link(c)[1]); + index -= left_count + 1; + } + } +} + +#endif + +#if CAVL_PARAM_FEATURE_ASSOC + +static CAvlAssoc CAvl_AssocSum (const CAvl *o, CAvlArg arg) +{ + if (o->root == CAvl_nulllink()) { + return CAVL_PARAM_VALUE_ASSOC_ZERO; + } + CAvlRef root = CAvlDeref(arg, o->root); + return CAvl_assoc(root); +} + +static CAvlAssoc CAvl_ExclusiveAssocPrefixSum (const CAvl *o, CAvlArg arg, CAvlRef node) +{ + ASSERT(node.link != CAvl_nulllink()) + ASSERT(o->root != CAvl_nulllink()) + + CAvlAssoc sum = (CAvl_link(node)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(node)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + + CAvlRef paren = CAvlDeref(arg, CAvl_parent(node)); + + for (CAvlRef c = node; paren.link != CAvl_nulllink(); c = paren, paren = CAvlDeref(arg, CAvl_parent(c))) { + if (c.link == CAvl_link(paren)[1]) { + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, paren); + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_val, sum); + if (CAvl_link(paren)[0] != CAvl_nulllink()) { + sum = CAVL_PARAM_FUN_ASSOC_OPER(arg, CAvl_assoc(CAvlDeref(arg, CAvl_link(paren)[0])), sum); + } + } + } + + return sum; +} + +static CAvlRef CAvl_FindLastExclusiveAssocPrefixSumLesserEqual (const CAvl *o, CAvlArg arg, CAvlAssoc sum, int (*sum_less) (void *, CAvlAssoc, CAvlAssoc), void *user) +{ + CAvlRef result = CAvl_nullref(); + CAvlRef c = CAvlDeref(arg, o->root); + CAvlAssoc sum_offset = CAVL_PARAM_VALUE_ASSOC_ZERO; + + while (c.link != CAvl_nulllink()) { + CAvlAssoc left_sum = (CAvl_link(c)[0] != CAvl_nulllink() ? CAvl_assoc(CAvlDeref(arg, CAvl_link(c)[0])) : CAVL_PARAM_VALUE_ASSOC_ZERO); + CAvlAssoc c_prefixsum = CAVL_PARAM_FUN_ASSOC_OPER(arg, sum_offset, left_sum); + + if (sum_less(user, sum, c_prefixsum)) { + c = CAvlDeref(arg, CAvl_link(c)[0]); + } else { + result = c; + CAvlAssoc c_val = CAVL_PARAM_FUN_ASSOC_VALUE(arg, c); + sum_offset = CAVL_PARAM_FUN_ASSOC_OPER(arg, c_prefixsum, c_val); + c = CAvlDeref(arg, CAvl_link(c)[1]); + } + } + + return result; +} + +#endif + +#include "CAvl_footer.h" diff --git a/external/badvpn_dns/structure/CHash.h b/external/badvpn_dns/structure/CHash.h new file mode 100644 index 00000000..45fef7a3 --- /dev/null +++ b/external/badvpn_dns/structure/CHash.h @@ -0,0 +1,39 @@ +/** + * @file CHash.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_CHASH_H +#define BADVPN_CHASH_H + +#include + +#include +#include +#include + +#endif diff --git a/external/badvpn_dns/structure/CHash_decl.h b/external/badvpn_dns/structure/CHash_decl.h new file mode 100644 index 00000000..d47702a1 --- /dev/null +++ b/external/badvpn_dns/structure/CHash_decl.h @@ -0,0 +1,59 @@ +/** + * @file CHash_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CHash_header.h" + +typedef struct { + CHashLink *buckets; + size_t num_buckets; +} CHash; + +typedef struct { + CHashEntry *ptr; + CHashLink link; +} CHashRef; + +static CHashLink CHashNullLink (void); +static CHashRef CHashNullRef (void); +static int CHashIsNullLink (CHashLink link); +static int CHashIsNullRef (CHashRef ref); +static CHashRef CHashDerefMayNull (CHashArg arg, CHashLink link); +static CHashRef CHashDerefNonNull (CHashArg arg, CHashLink link); + +static int CHash_Init (CHash *o, size_t num_buckets); +static void CHash_Free (CHash *o); +static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing); +static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry); +static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry); +static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key); +static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry); +static int CHash_MultiplyBuckets (CHash *o, CHashArg arg, int exp); +static void CHash_Verify (const CHash *o, CHashArg arg); + +#include "CHash_footer.h" diff --git a/external/badvpn_dns/structure/CHash_footer.h b/external/badvpn_dns/structure/CHash_footer.h new file mode 100644 index 00000000..cb95dafd --- /dev/null +++ b/external/badvpn_dns/structure/CHash_footer.h @@ -0,0 +1,74 @@ +/** + * @file CHash_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// preprocessor inputs +#undef CHASH_PARAM_NAME +#undef CHASH_PARAM_ENTRY +#undef CHASH_PARAM_LINK +#undef CHASH_PARAM_KEY +#undef CHASH_PARAM_ARG +#undef CHASH_PARAM_NULL +#undef CHASH_PARAM_DEREF +#undef CHASH_PARAM_ENTRYHASH +#undef CHASH_PARAM_KEYHASH +#undef CHASH_PARAM_ENTRYHASH_IS_CHEAP +#undef CHASH_PARAM_COMPARE_ENTRIES +#undef CHASH_PARAM_COMPARE_KEY_ENTRY +#undef CHASH_PARAM_ENTRY_NEXT + +// types +#undef CHash +#undef CHashEntry +#undef CHashLink +#undef CHashRef +#undef CHashArg +#undef CHashKey + +// non-object public functions +#undef CHashNullLink +#undef CHashNullRef +#undef CHashIsNullLink +#undef CHashIsNullRef +#undef CHashDerefMayNull +#undef CHashDerefNonNull + +// public functions +#undef CHash_Init +#undef CHash_Free +#undef CHash_Insert +#undef CHash_InsertMulti +#undef CHash_Remove +#undef CHash_Lookup +#undef CHash_GetNextEqual +#undef CHash_MultiplyBuckets +#undef CHash_Verify + +// private things +#undef CHash_next +#undef CHash_assert_valid_entry diff --git a/external/badvpn_dns/structure/CHash_header.h b/external/badvpn_dns/structure/CHash_header.h new file mode 100644 index 00000000..27800aca --- /dev/null +++ b/external/badvpn_dns/structure/CHash_header.h @@ -0,0 +1,78 @@ +/** + * @file CHash_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// CHASH_PARAM_NAME - name of this data structure +// CHASH_PARAM_ENTRY - type of entry +// CHASH_PARAM_LINK - type of entry link (usually a pointer or index to an array) +// CHASH_PARAM_KEY - type of key +// CHASH_PARAM_ARG - type of argument pass through to comparisons +// CHASH_PARAM_NULL - invalid link +// CHASH_PARAM_DEREF(arg, link) - dereference a non-null link +// CHASH_PARAM_ENTRYHASH(arg, entry) - hash function for entries; returns size_t +// CHASH_PARAM_KEYHASH(arg, key) - hash function for keys; returns size_t +// CHASH_PARAM_ENTRYHASH_IS_CHEAP - define to 1 if CHASH_PARAM_ENTRYHASH is cheap (e.g. hashes are precomputed) +// CHASH_PARAM_COMPARE_ENTRIES(arg, entry1, entry2) - compares two entries; returns 1 for equality, 0 otherwise +// CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key1, entry2) - compares key and entry; returns 1 for equality, 0 otherwise +// CHASH_PARAM_ENTRY_NEXT - next member in entry + +#ifndef BADVPN_CHASH_H +#error CHash.h has not been included +#endif + +// types +#define CHash CHASH_PARAM_NAME +#define CHashEntry CHASH_PARAM_ENTRY +#define CHashLink CHASH_PARAM_LINK +#define CHashRef MERGE(CHash, Ref) +#define CHashArg CHASH_PARAM_ARG +#define CHashKey CHASH_PARAM_KEY + +// non-object public functions +#define CHashNullLink MERGE(CHash, NullLink) +#define CHashNullRef MERGE(CHash, NullRef) +#define CHashIsNullLink MERGE(CHash, IsNullLink) +#define CHashIsNullRef MERGE(CHash, IsNullRef) +#define CHashDerefMayNull MERGE(CHash, DerefMayNull) +#define CHashDerefNonNull MERGE(CHash, DerefNonNull) + +// public functions +#define CHash_Init MERGE(CHash, _Init) +#define CHash_Free MERGE(CHash, _Free) +#define CHash_Insert MERGE(CHash, _Insert) +#define CHash_InsertMulti MERGE(CHash, _InsertMulti) +#define CHash_Remove MERGE(CHash, _Remove) +#define CHash_Lookup MERGE(CHash, _Lookup) +#define CHash_GetNextEqual MERGE(CHash, _GetNextEqual) +#define CHash_MultiplyBuckets MERGE(CHash, _MultiplyBuckets) +#define CHash_Verify MERGE(CHash, _Verify) + +// private things +#define CHash_next(entry) ((entry).ptr->CHASH_PARAM_ENTRY_NEXT) +#define CHash_assert_valid_entry MERGE(CHash, _assert_valid_entry) diff --git a/external/badvpn_dns/structure/CHash_impl.h b/external/badvpn_dns/structure/CHash_impl.h new file mode 100644 index 00000000..0bded845 --- /dev/null +++ b/external/badvpn_dns/structure/CHash_impl.h @@ -0,0 +1,312 @@ +/** + * @file CHash_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "CHash_header.h" + +static void CHash_assert_valid_entry (CHashArg arg, CHashRef entry) +{ + ASSERT(entry.link != CHashNullLink()) + ASSERT(entry.ptr == CHASH_PARAM_DEREF(arg, entry.link)) +} + +static CHashLink CHashNullLink (void) +{ + return CHASH_PARAM_NULL; +} + +static CHashRef CHashNullRef (void) +{ + CHashRef entry = {NULL, CHashNullLink()}; + return entry; +} + +static int CHashIsNullLink (CHashLink link) +{ + return (link == CHashNullLink()); +} + +static int CHashIsNullRef (CHashRef ref) +{ + return CHashIsNullLink(ref.link); +} + +static CHashRef CHashDerefMayNull (CHashArg arg, CHashLink link) +{ + if (link == CHashNullLink()) { + return CHashNullRef(); + } + + CHashRef entry = {CHASH_PARAM_DEREF(arg, link), link}; + ASSERT(entry.ptr) + + return entry; +} + +static CHashRef CHashDerefNonNull (CHashArg arg, CHashLink link) +{ + ASSERT(link != CHashNullLink()) + + CHashRef entry = {CHASH_PARAM_DEREF(arg, link), link}; + ASSERT(entry.ptr) + + return entry; +} + +static int CHash_Init (CHash *o, size_t num_buckets) +{ + if (num_buckets == 0) { + num_buckets = 1; + } + + o->num_buckets = num_buckets; + + o->buckets = (CHashLink *)BAllocArray(o->num_buckets, sizeof(o->buckets[0])); + if (!o->buckets) { + return 0; + } + + for (size_t i = 0; i < o->num_buckets; i++) { + o->buckets[i] = CHashNullLink(); + } + + return 1; +} + +static void CHash_Free (CHash *o) +{ + BFree(o->buckets); +} + +static int CHash_Insert (CHash *o, CHashArg arg, CHashRef entry, CHashRef *out_existing) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + if (CHASH_PARAM_COMPARE_ENTRIES(arg, cur, entry)) { + if (out_existing) { + *out_existing = cur; + } + return 0; + } + link = CHash_next(cur); + } + + CHash_next(entry) = o->buckets[index]; + o->buckets[index] = entry.link; + + return 1; +} + +static void CHash_InsertMulti (CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + if (CHASH_PARAM_COMPARE_ENTRIES(arg, cur, entry)) { + break; + } + prev = cur; + link = CHash_next(cur); + } + + if (link == CHashNullLink() || prev.link == CHashNullLink()) { + CHash_next(entry) = o->buckets[index]; + o->buckets[index] = entry.link; + } else { + CHash_next(entry) = link; + CHash_next(prev) = entry.link; + } +} + +static void CHash_Remove (CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + size_t index = CHASH_PARAM_ENTRYHASH(arg, entry) % o->num_buckets; + + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[index]; + while (link != entry.link) { + CHashRef cur = CHashDerefNonNull(arg, link); + prev = cur; + link = CHash_next(cur); + ASSERT(link != CHashNullLink()) + } + + if (prev.link == CHashNullLink()) { + o->buckets[index] = CHash_next(entry); + } else { + CHash_next(prev) = CHash_next(entry); + } +} + +static CHashRef CHash_Lookup (const CHash *o, CHashArg arg, CHashKey key) +{ + size_t hash = CHASH_PARAM_KEYHASH(arg, key); + size_t index = hash % o->num_buckets; + + CHashLink link = o->buckets[index]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); +#if CHASH_PARAM_ENTRYHASH_IS_CHEAP + if (CHASH_PARAM_ENTRYHASH(arg, cur) == hash && CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key, cur)) { +#else + if (CHASH_PARAM_COMPARE_KEY_ENTRY(arg, key, cur)) { +#endif + return cur; + } + link = CHash_next(cur); + } + + return CHashNullRef(); +} + +static CHashRef CHash_GetNextEqual (const CHash *o, CHashArg arg, CHashRef entry) +{ + CHash_assert_valid_entry(arg, entry); + + CHashLink next = CHash_next(entry); + + if (next == CHashNullLink()) { + return CHashNullRef(); + } + + CHashRef next_ref = CHashDerefNonNull(arg, next); + if (!CHASH_PARAM_COMPARE_ENTRIES(arg, next_ref, entry)) { + return CHashNullRef(); + } + + return next_ref; +} + +static int CHash_MultiplyBuckets (CHash *o, CHashArg arg, int exp) +{ + ASSERT(exp > 0) + + size_t new_num_buckets = o->num_buckets; + while (exp-- > 0) { + if (new_num_buckets > SIZE_MAX / 2) { + return 0; + } + new_num_buckets *= 2; + } + + CHashLink *new_buckets = (CHashLink *)BReallocArray(o->buckets, new_num_buckets, sizeof(new_buckets[0])); + if (!new_buckets) { + return 0; + } + o->buckets = new_buckets; + + for (size_t i = o->num_buckets; i < new_num_buckets; i++) { + o->buckets[i] = CHashNullLink(); + } + + for (size_t i = 0; i < o->num_buckets; i++) { + CHashRef prev = CHashNullRef(); + CHashLink link = o->buckets[i]; + + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + link = CHash_next(cur); + + size_t new_index = CHASH_PARAM_ENTRYHASH(arg, cur) % new_num_buckets; + if (new_index == i) { + prev = cur; + continue; + } + + if (CHashIsNullRef(prev)) { + o->buckets[i] = CHash_next(cur); + } else { + CHash_next(prev) = CHash_next(cur); + } + + CHash_next(cur) = o->buckets[new_index]; + o->buckets[new_index] = cur.link; + } + } + + for (size_t i = o->num_buckets; i < new_num_buckets; i++) { + CHashLink new_bucket_link = CHashNullLink(); + + CHashLink link = o->buckets[i]; + while (link != CHashNullLink()) { + CHashRef cur = CHashDerefNonNull(arg, link); + link = CHash_next(cur); + + CHash_next(cur) = new_bucket_link; + new_bucket_link = cur.link; + } + + o->buckets[i] = new_bucket_link; + } + + o->num_buckets = new_num_buckets; + + return 1; +} + +static void CHash_Verify (const CHash *o, CHashArg arg) +{ + ASSERT_FORCE(o->num_buckets > 0) + ASSERT_FORCE(o->buckets) + + for (size_t i = 0; i < o->num_buckets; i++) { + CHashRef cur = CHashDerefMayNull(arg, o->buckets[i]); + CHashRef same_first = cur; + + while (!CHashIsNullRef(cur)) { + size_t index = CHASH_PARAM_ENTRYHASH(arg, cur) % o->num_buckets; + ASSERT_FORCE(index == i) + + if (!CHASH_PARAM_COMPARE_ENTRIES(arg, cur, same_first)) { + same_first = cur; + } + + CHashRef ccur = CHashDerefNonNull(arg, o->buckets[i]); + while (ccur.link != same_first.link) { + ASSERT_FORCE(!CHASH_PARAM_COMPARE_ENTRIES(arg, ccur, cur)) + ccur = CHashDerefMayNull(arg, CHash_next(ccur)); + } + + cur = CHashDerefMayNull(arg, CHash_next(cur)); + } + } +} + +#include "CHash_footer.h" diff --git a/external/badvpn_dns/structure/ChunkBuffer2.h b/external/badvpn_dns/structure/ChunkBuffer2.h new file mode 100644 index 00000000..98073ad1 --- /dev/null +++ b/external/badvpn_dns/structure/ChunkBuffer2.h @@ -0,0 +1,317 @@ +/** + * @file ChunkBuffer2.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Circular packet buffer + */ + +#ifndef BADVPN_STRUCTURE_CHUNKBUFFER2_H +#define BADVPN_STRUCTURE_CHUNKBUFFER2_H + +#include +#include +#include + +#include +#include + +#ifndef NDEBUG +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) _ChunkBuffer2_assert_buffer(_buf); +#define CHUNKBUFFER2_ASSERT_IO(_buf) _ChunkBuffer2_assert_io(_buf); +#else +#define CHUNKBUFFER2_ASSERT_BUFFER(_buf) +#define CHUNKBUFFER2_ASSERT_IO(_buf) +#endif + +struct ChunkBuffer2_block { + int len; +}; + +typedef struct { + struct ChunkBuffer2_block *buffer; + int size; + int wrap; + int start; + int used; + int mtu; + uint8_t *input_dest; + int input_avail; + uint8_t *output_dest; + int output_avail; +} ChunkBuffer2; + +// calculates a buffer size needed to hold at least 'num' packets long at least 'chunk_len' +static int ChunkBuffer2_calc_blocks (int chunk_len, int num); + +// initialize +static void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu); + +// submit a packet written to the buffer +static void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len); + +// remove the first packet +static void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf); + +static int _ChunkBuffer2_end (ChunkBuffer2 *buf) +{ + if (buf->used >= buf->wrap - buf->start) { + return (buf->used - (buf->wrap - buf->start)); + } else { + return (buf->start + buf->used); + } +} + +#ifndef NDEBUG + +static void _ChunkBuffer2_assert_buffer (ChunkBuffer2 *buf) +{ + ASSERT(buf->size > 0) + ASSERT(buf->wrap > 0) + ASSERT(buf->wrap <= buf->size) + ASSERT(buf->start >= 0) + ASSERT(buf->start < buf->wrap) + ASSERT(buf->used >= 0) + ASSERT(buf->used <= buf->wrap) + ASSERT(buf->wrap == buf->size || buf->used >= buf->wrap - buf->start) + ASSERT(buf->mtu >= 0) +} + +static void _ChunkBuffer2_assert_io (ChunkBuffer2 *buf) +{ + // check input + + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } else { + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + ASSERT(buf->input_dest == (uint8_t *)&buf->buffer[end + 1]) + ASSERT(buf->input_avail == (free - 1) * sizeof(struct ChunkBuffer2_block)) + } else { + // no space + ASSERT(!buf->input_dest) + ASSERT(buf->input_avail == -1) + } + } + + // check output + + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(buf->output_dest == (uint8_t *)&buf->buffer[buf->start + 1]) + ASSERT(buf->output_avail == datalen) + } else { + ASSERT(!buf->output_dest) + ASSERT(buf->output_avail == -1) + } +} + +#endif + +static void _ChunkBuffer2_update_input (ChunkBuffer2 *buf) +{ + int end = _ChunkBuffer2_end(buf); + + if (buf->size - end - 1 < buf->mtu) { + // it will never be possible to write a MTU long packet here + buf->input_dest = NULL; + buf->input_avail = -1; + return; + } + + // calculate number of free blocks + int free; + if (buf->used >= buf->wrap - buf->start) { + free = buf->start - end; + } else { + free = buf->size - end; + } + + if (free > 0) { + // got space at least for a header. More space will become available as packets are + // read from the buffer, up to MTU. + buf->input_dest = (uint8_t *)&buf->buffer[end + 1]; + buf->input_avail = (free - 1) * sizeof(struct ChunkBuffer2_block); + } else { + // no space + buf->input_dest = NULL; + buf->input_avail = -1; + } +} + +static void _ChunkBuffer2_update_output (ChunkBuffer2 *buf) +{ + if (buf->used > 0) { + int datalen = buf->buffer[buf->start].len; + ASSERT(datalen >= 0) +#ifndef NDEBUG + int blocklen = bdivide_up(datalen, sizeof(struct ChunkBuffer2_block)); + ASSERT(blocklen <= buf->used - 1) + ASSERT(blocklen <= buf->wrap - buf->start - 1) +#endif + buf->output_dest = (uint8_t *)&buf->buffer[buf->start + 1]; + buf->output_avail = datalen; + } else { + buf->output_dest = NULL; + buf->output_avail = -1; + } +} + +int ChunkBuffer2_calc_blocks (int chunk_len, int num) +{ + int chunk_data_blocks = bdivide_up(chunk_len, sizeof(struct ChunkBuffer2_block)); + + if (chunk_data_blocks > INT_MAX - 1) { + return -1; + } + int chunk_blocks = 1 + chunk_data_blocks; + + if (num > INT_MAX - 1) { + return -1; + } + int num_chunks = num + 1; + + if (chunk_blocks > INT_MAX / num_chunks) { + return -1; + } + int blocks = chunk_blocks * num_chunks; + + return blocks; +} + +void ChunkBuffer2_Init (ChunkBuffer2 *buf, struct ChunkBuffer2_block *buffer, int blocks, int mtu) +{ + ASSERT(blocks > 0) + ASSERT(mtu >= 0) + + buf->buffer = buffer; + buf->size = blocks; + buf->wrap = blocks; + buf->start = 0; + buf->used = 0; + buf->mtu = bdivide_up(mtu, sizeof(struct ChunkBuffer2_block)); + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + _ChunkBuffer2_update_input(buf); + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_SubmitPacket (ChunkBuffer2 *buf, int len) +{ + ASSERT(buf->input_dest) + ASSERT(len >= 0) + ASSERT(len <= buf->input_avail) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + int end = _ChunkBuffer2_end(buf); + int blocklen = bdivide_up(len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->size - end - 1) + ASSERT(buf->used < buf->wrap - buf->start || blocklen <= buf->start - end - 1) + + buf->buffer[end].len = len; + buf->used += 1 + blocklen; + + if (buf->used <= buf->wrap - buf->start && buf->mtu > buf->size - (end + 1 + blocklen) - 1) { + buf->wrap = end + 1 + blocklen; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + _ChunkBuffer2_update_input(buf); + + // update output + if (buf->used == 1 + blocklen) { + _ChunkBuffer2_update_output(buf); + } + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +void ChunkBuffer2_ConsumePacket (ChunkBuffer2 *buf) +{ + ASSERT(buf->output_dest) + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + CHUNKBUFFER2_ASSERT_IO(buf) + + ASSERT(1 <= buf->wrap - buf->start) + ASSERT(1 <= buf->used) + + int blocklen = bdivide_up(buf->buffer[buf->start].len, sizeof(struct ChunkBuffer2_block)); + + ASSERT(blocklen <= buf->wrap - buf->start - 1) + ASSERT(blocklen <= buf->used - 1) + + int data_wrapped = (buf->used >= buf->wrap - buf->start); + + buf->start += 1 + blocklen; + buf->used -= 1 + blocklen; + if (buf->start == buf->wrap) { + buf->start = 0; + buf->wrap = buf->size; + } + + CHUNKBUFFER2_ASSERT_BUFFER(buf) + + // update input + if (data_wrapped) { + _ChunkBuffer2_update_input(buf); + } + + // update output + _ChunkBuffer2_update_output(buf); + + CHUNKBUFFER2_ASSERT_IO(buf) +} + +#endif diff --git a/external/badvpn_dns/structure/IndexedList.h b/external/badvpn_dns/structure/IndexedList.h new file mode 100644 index 00000000..ca611e9b --- /dev/null +++ b/external/badvpn_dns/structure/IndexedList.h @@ -0,0 +1,225 @@ +/** + * @file IndexedList.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A data structure similar to a list, but with efficient index-based access. + */ + +#ifndef BADVPN_INDEXEDLIST_H +#define BADVPN_INDEXEDLIST_H + +#include +#include + +#include +#include +#include + +typedef struct IndexedList_s IndexedList; +typedef struct IndexedListNode_s IndexedListNode; + +typedef IndexedListNode *IndexedList__tree_link; + +#include "IndexedList_tree.h" +#include + +struct IndexedList_s { + IndexedList__Tree tree; +}; + +struct IndexedListNode_s { + IndexedListNode *tree_child[2]; + IndexedListNode *tree_parent; + int8_t tree_balance; + uint64_t tree_count; +}; + +/** + * Initializes the indexed list. + * + * @param o uninitialized list object to initialize + */ +static void IndexedList_Init (IndexedList *o); + +/** + * Inserts a node into the indexed list. + * + * @param o indexed list to insert into + * @param node uninitialized node to insert + * @param index index to insert at (starting with zero). Any existing elements + * at or after this index will be shifted forward, i.e. their + * indices will be incremented by one. Must be <=count. + */ +static void IndexedList_InsertAt (IndexedList *o, IndexedListNode *node, uint64_t index); + +/** + * Removes a nove from the indexed list. + * + * @param o indexed list to remove from + * @param node node in the list to remove + */ +static void IndexedList_Remove (IndexedList *o, IndexedListNode *node); + +/** + * Returns the number of nodes in the indexed list. + * + * @param o indexed list + * @return number of nodes + */ +static uint64_t IndexedList_Count (IndexedList *o); + +/** + * Returns the index of a node in the indexed list. + * + * @param o indexed list + * @param node node in the list to get index of + * @return index of the node + */ +static uint64_t IndexedList_IndexOf (IndexedList *o, IndexedListNode *node); + +/** + * Returns the node at the specified index in the indexed list. + * + * @param o indexed list + * @param index index of the node to return. Must be < count. + * @return node at the specified index + */ +static IndexedListNode * IndexedList_GetAt (IndexedList *o, uint64_t index); + +/** + * Returns the first node, or NULL if the list is empty. + * + * @param o indexed list + * @return first node, or NULL + */ +static IndexedListNode * IndexedList_GetFirst (IndexedList *o); + +/** + * Returns the last node, or NULL if the list is empty. + * + * @param o indexed list + * @return last node, or NULL + */ +static IndexedListNode * IndexedList_GetLast (IndexedList *o); + +/** + * Returns the next node of a given node, or NULL this is the last node. + * + * @param o indexed list + * @param node existing node + * @return next node, or NULL + */ +static IndexedListNode * IndexedList_GetNext (IndexedList *o, IndexedListNode *node); + +/** + * Returns the previous node of a given node, or NULL this is the first node. + * + * @param o indexed list + * @param node existing node + * @return previous node, or NULL + */ +static IndexedListNode * IndexedList_GetPrev (IndexedList *o, IndexedListNode *node); + +#include "IndexedList_tree.h" +#include + +static IndexedListNode * IndexedList__deref (IndexedList__TreeRef ref) +{ + return ref.link; +} + +static void IndexedList_Init (IndexedList *o) +{ + IndexedList__Tree_Init(&o->tree); +} + +static void IndexedList_InsertAt (IndexedList *o, IndexedListNode *node, uint64_t index) +{ + ASSERT(index <= IndexedList__Tree_Count(&o->tree, 0)) + ASSERT(IndexedList__Tree_Count(&o->tree, 0) < UINT64_MAX - 1) + + uint64_t orig_count = IndexedList__Tree_Count(&o->tree, 0); + B_USE(orig_count) + + IndexedList__Tree_InsertAt(&o->tree, 0, IndexedList__TreeDeref(0, node), index); + + ASSERT(IndexedList__Tree_IndexOf(&o->tree, 0, IndexedList__TreeDeref(0, node)) == index) + ASSERT(IndexedList__Tree_Count(&o->tree, 0) == orig_count + 1) +} + +static void IndexedList_Remove (IndexedList *o, IndexedListNode *node) +{ + IndexedList__Tree_Remove(&o->tree, 0, IndexedList__TreeDeref(0, node)); +} + +static uint64_t IndexedList_Count (IndexedList *o) +{ + return IndexedList__Tree_Count(&o->tree, 0); +} + +static uint64_t IndexedList_IndexOf (IndexedList *o, IndexedListNode *node) +{ + return IndexedList__Tree_IndexOf(&o->tree, 0, IndexedList__TreeDeref(0, node)); +} + +static IndexedListNode * IndexedList_GetAt (IndexedList *o, uint64_t index) +{ + ASSERT(index < IndexedList__Tree_Count(&o->tree, 0)) + + IndexedList__TreeRef ref = IndexedList__Tree_GetAt(&o->tree, 0, index); + ASSERT(!IndexedList__TreeIsNullRef(ref)) + + return ref.ptr; +} + +static IndexedListNode * IndexedList_GetFirst (IndexedList *o) +{ + return IndexedList__deref(IndexedList__Tree_GetFirst(&o->tree, 0)); +} + +static IndexedListNode * IndexedList_GetLast (IndexedList *o) +{ + return IndexedList__deref(IndexedList__Tree_GetLast(&o->tree, 0)); +} + +static IndexedListNode * IndexedList_GetNext (IndexedList *o, IndexedListNode *node) +{ + ASSERT(node) + + return IndexedList__deref(IndexedList__Tree_GetNext(&o->tree, 0, IndexedList__TreeDeref(0, node))); +} + +static IndexedListNode * IndexedList_GetPrev (IndexedList *o, IndexedListNode *node) +{ + ASSERT(node) + + return IndexedList__deref(IndexedList__Tree_GetPrev(&o->tree, 0, IndexedList__TreeDeref(0, node))); +} + +#endif diff --git a/external/badvpn_dns/structure/IndexedList_tree.h b/external/badvpn_dns/structure/IndexedList_tree.h new file mode 100644 index 00000000..130f00fd --- /dev/null +++ b/external/badvpn_dns/structure/IndexedList_tree.h @@ -0,0 +1,15 @@ +#define CAVL_PARAM_NAME IndexedList__Tree +#define CAVL_PARAM_FEATURE_COUNTS 1 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 1 +#define CAVL_PARAM_FEATURE_NOKEYS 0 +#define CAVL_PARAM_TYPE_ENTRY IndexedListNode +#define CAVL_PARAM_TYPE_LINK IndexedList__tree_link +#define CAVL_PARAM_TYPE_ARG int +#define CAVL_PARAM_TYPE_COUNT uint64_t +#define CAVL_PARAM_VALUE_COUNT_MAX UINT64_MAX +#define CAVL_PARAM_VALUE_NULL ((IndexedList__tree_link)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_MEMBER_CHILD tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent +#define CAVL_PARAM_MEMBER_COUNT tree_count diff --git a/external/badvpn_dns/structure/LinkedList0.h b/external/badvpn_dns/structure/LinkedList0.h new file mode 100644 index 00000000..feef4d7b --- /dev/null +++ b/external/badvpn_dns/structure/LinkedList0.h @@ -0,0 +1,202 @@ +/** + * @file LinkedList0.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Very simple doubly linked list, with only a 'first' pointer an no 'last' + * pointer. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST0_H +#define BADVPN_STRUCTURE_LINKEDLIST0_H + +#include + +#include + +/** + * Linked list node. + */ +typedef struct LinkedList0Node_t +{ + struct LinkedList0Node_t *p; + struct LinkedList0Node_t *n; +} LinkedList0Node; + +/** + * Simple doubly linked list. + */ +typedef struct +{ + LinkedList0Node *first; +} LinkedList0; + +/** + * Initializes the linked list. + * + * @param list list to initialize + */ +static void LinkedList0_Init (LinkedList0 *list); + +/** + * Determines if the list is empty. + * + * @param list the list + * @return 1 if empty, 0 if not + */ +static int LinkedList0_IsEmpty (LinkedList0 *list); + +/** + * Returns the first node of the list. + * + * @param list the list + * @return first node of the list, or NULL if the list is empty + */ +static LinkedList0Node * LinkedList0_GetFirst (LinkedList0 *list); + +/** + * Inserts a node to the beginning of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList0_Prepend (LinkedList0 *list, LinkedList0Node *node); + +/** + * Inserts a node before a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert before + */ +static void LinkedList0_InsertBefore (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target); + +/** + * Inserts a node after a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert after + */ +static void LinkedList0_InsertAfter (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target); + +/** + * Removes a node from the list. + * + * @param list the list + * @param node node to remove + */ +static void LinkedList0_Remove (LinkedList0 *list, LinkedList0Node *node); + +/** + * Returns the next node of a given node. + * + * @param node reference node + * @return next node, or NULL if none + */ +static LinkedList0Node * LinkedList0Node_Next (LinkedList0Node *node); + +/** + * Returns the previous node of a given node. + * + * @param node reference node + * @return previous node, or NULL if none + */ +static LinkedList0Node * LinkedList0Node_Prev (LinkedList0Node *node); + +void LinkedList0_Init (LinkedList0 *list) +{ + list->first = NULL; +} + +int LinkedList0_IsEmpty (LinkedList0 *list) +{ + return (!list->first); +} + +LinkedList0Node * LinkedList0_GetFirst (LinkedList0 *list) +{ + return (list->first); +} + +void LinkedList0_Prepend (LinkedList0 *list, LinkedList0Node *node) +{ + node->p = NULL; + node->n = list->first; + if (list->first) { + list->first->p = node; + } + list->first = node; +} + +void LinkedList0_InsertBefore (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target) +{ + node->p = target->p; + node->n = target; + if (target->p) { + target->p->n = node; + } else { + list->first = node; + } + target->p = node; +} + +void LinkedList0_InsertAfter (LinkedList0 *list, LinkedList0Node *node, LinkedList0Node *target) +{ + node->p = target; + node->n = target->n; + if (target->n) { + target->n->p = node; + } + target->n = node; +} + +void LinkedList0_Remove (LinkedList0 *list, LinkedList0Node *node) +{ + // remove from list + if (node->p) { + node->p->n = node->n; + } else { + list->first = node->n; + } + if (node->n) { + node->n->p = node->p; + } +} + +LinkedList0Node * LinkedList0Node_Next (LinkedList0Node *node) +{ + return node->n; +} + +LinkedList0Node * LinkedList0Node_Prev (LinkedList0Node *node) +{ + return node->p; +} + +#endif diff --git a/external/badvpn_dns/structure/LinkedList1.h b/external/badvpn_dns/structure/LinkedList1.h new file mode 100644 index 00000000..bdb7161c --- /dev/null +++ b/external/badvpn_dns/structure/LinkedList1.h @@ -0,0 +1,275 @@ +/** + * @file LinkedList1.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Simple doubly linked list. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST1_H +#define BADVPN_STRUCTURE_LINKEDLIST1_H + +#include + +#include + +/** + * Linked list node. + */ +typedef struct LinkedList1Node_t +{ + struct LinkedList1Node_t *p; + struct LinkedList1Node_t *n; +} LinkedList1Node; + +/** + * Simple doubly linked list. + */ +typedef struct +{ + LinkedList1Node *first; + LinkedList1Node *last; +} LinkedList1; + +/** + * Initializes the linked list. + * + * @param list list to initialize + */ +static void LinkedList1_Init (LinkedList1 *list); + +/** + * Determines if the list is empty. + * + * @param list the list + * @return 1 if empty, 0 if not + */ +static int LinkedList1_IsEmpty (LinkedList1 *list); + +/** + * Returns the first node of the list. + * + * @param list the list + * @return first node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list); + +/** + * Returns the last node of the list. + * + * @param list the list + * @return last node of the list, or NULL if the list is empty + */ +static LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list); + +/** + * Inserts a node to the beginning of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node to the end of the list. + * + * @param list the list + * @param node uninitialized node to insert + */ +static void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts a node before a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert before + */ +static void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Inserts a node after a given node. + * + * @param list the list + * @param node uninitialized node to insert + * @param target node in the list to insert after + */ +static void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target); + +/** + * Removes a node from the list. + * + * @param list the list + * @param node node to remove + */ +static void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node); + +/** + * Inserts the nodes in the list \a ins_list into this list, after the node \a target. + * If \a target is NULL, the nodes are inserted to the beginning. + * Note that because the first and last node in \a ins_list are modified + * (unless the list is empty), \a ins_list is invalidated and must no longer + * be used to access the inserted nodes. + */ +static void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target); + +/** + * Returns the next node of a given node. + * + * @param node reference node + * @return next node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node); + +/** + * Returns the previous node of a given node. + * + * @param node reference node + * @return previous node, or NULL if none + */ +static LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node); + +void LinkedList1_Init (LinkedList1 *list) +{ + list->first = NULL; + list->last = NULL; +} + +int LinkedList1_IsEmpty (LinkedList1 *list) +{ + return (!list->first); +} + +LinkedList1Node * LinkedList1_GetFirst (LinkedList1 *list) +{ + return (list->first); +} + +LinkedList1Node * LinkedList1_GetLast (LinkedList1 *list) +{ + return (list->last); +} + +void LinkedList1_Prepend (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = NULL; + node->n = list->first; + if (list->first) { + list->first->p = node; + } else { + list->last = node; + } + list->first = node; +} + +void LinkedList1_Append (LinkedList1 *list, LinkedList1Node *node) +{ + node->p = list->last; + node->n = NULL; + if (list->last) { + list->last->n = node; + } else { + list->first = node; + } + list->last = node; +} + +void LinkedList1_InsertBefore (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target->p; + node->n = target; + if (target->p) { + target->p->n = node; + } else { + list->first = node; + } + target->p = node; +} + +void LinkedList1_InsertAfter (LinkedList1 *list, LinkedList1Node *node, LinkedList1Node *target) +{ + node->p = target; + node->n = target->n; + if (target->n) { + target->n->p = node; + } else { + list->last = node; + } + target->n = node; +} + +void LinkedList1_Remove (LinkedList1 *list, LinkedList1Node *node) +{ + // remove from list + if (node->p) { + node->p->n = node->n; + } else { + list->first = node->n; + } + if (node->n) { + node->n->p = node->p; + } else { + list->last = node->p; + } +} + +void LinkedList1_InsertListAfter (LinkedList1 *list, LinkedList1 ins_list, LinkedList1Node *target) +{ + if (!ins_list.first) { + return; + } + + LinkedList1Node *t_next = (target ? target->n : list->first); + + ins_list.first->p = target; + ins_list.last->n = t_next; + + if (target) { + target->n = ins_list.first; + } else { + list->first = ins_list.first; + } + + if (t_next) { + t_next->p = ins_list.last; + } else { + list->last = ins_list.last; + } +} + +LinkedList1Node * LinkedList1Node_Next (LinkedList1Node *node) +{ + return node->n; +} + +LinkedList1Node * LinkedList1Node_Prev (LinkedList1Node *node) +{ + return node->p; +} + +#endif diff --git a/external/badvpn_dns/structure/LinkedList3.h b/external/badvpn_dns/structure/LinkedList3.h new file mode 100644 index 00000000..8f54cdb5 --- /dev/null +++ b/external/badvpn_dns/structure/LinkedList3.h @@ -0,0 +1,362 @@ +/** + * @file LinkedList3.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Doubly linked list that support multiple iterations and removing + * aritrary elements during iteration, without a central object to + * represent the list. + */ + +#ifndef BADVPN_STRUCTURE_LINKEDLIST3_H +#define BADVPN_STRUCTURE_LINKEDLIST3_H + +#include +#include + +#include + +struct _LinkedList3Iterator; + +/** + * Linked list node. + */ +typedef struct _LinkedList3Node { + struct _LinkedList3Node *p; + struct _LinkedList3Node *n; + struct _LinkedList3Iterator *it; +} LinkedList3Node; + +/** + * Linked list iterator. + */ +typedef struct _LinkedList3Iterator { + int8_t dir; + struct _LinkedList3Node *e; + struct _LinkedList3Iterator *pi; + struct _LinkedList3Iterator *ni; +} LinkedList3Iterator; + +/** + * Initializes a list node to form a new list consisting of a + * single node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + */ +static void LinkedList3Node_InitLonely (LinkedList3Node *node); + +/** + * Initializes a list node to go after an existing node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + * @param ref existing list node + */ +static void LinkedList3Node_InitAfter (LinkedList3Node *node, LinkedList3Node *ref); + +/** + * Initializes a list node to go before an existing node. + * + * @param node list node structure to initialize. The node must remain + * available until it is freed with {@link LinkedList3Node_Free}, + * or the list is no longer required. + * @param ref existing list node + */ +static void LinkedList3Node_InitBefore (LinkedList3Node *node, LinkedList3Node *ref); + +/** + * Frees a list node, removing it a list (if there were other nodes + * in the list). + * + * @param node list node to free + */ +static void LinkedList3Node_Free (LinkedList3Node *node); + +/** + * Determines if a list node is a single node in a list. + * + * @param node list node + * @return 1 if the node ia a single node, 0 if not + */ +static int LinkedList3Node_IsLonely (LinkedList3Node *node); + +/** + * Returnes the node preceding this node (if there is one), + * the node following this node (if there is one), or NULL, + * respectively. + * + * @param node list node + * @return neighbour node or NULL if none + */ +static LinkedList3Node * LinkedList3Node_PrevOrNext (LinkedList3Node *node); + +/** + * Returnes the node following this node (if there is one), + * the node preceding this node (if there is one), or NULL, + * respectively. + * + * @param node list node + * @return neighbour node or NULL if none + */ +static LinkedList3Node * LinkedList3Node_NextOrPrev (LinkedList3Node *node); + +/** + * Returns the node preceding this node, or NULL if there is none. + * + * @param node list node + * @return left neighbour, or NULL if none + */ +static LinkedList3Node * LinkedList3Node_Prev (LinkedList3Node *node); + +/** + * Returns the node following this node, or NULL if there is none. + * + * @param node list node + * @return right neighbour, or NULL if none + */ +static LinkedList3Node * LinkedList3Node_Next (LinkedList3Node *node); + +/** + * Returns the first node in the list which this node is part of. + * It is found by iterating the list from this node to the beginning. + * + * @param node list node + * @return first node in the list + */ +static LinkedList3Node * LinkedList3Node_First (LinkedList3Node *node); + +/** + * Returns the last node in the list which this node is part of. + * It is found by iterating the list from this node to the end. + * + * @param node list node + * @return last node in the list + */ +static LinkedList3Node * LinkedList3Node_Last (LinkedList3Node *node); + +/** + * Initializes a linked list iterator. + * The iterator structure must remain available until either of these occurs: + * - the list is no longer needed, or + * - the iterator is freed with {@link LinkedList3Iterator_Free}, or + * - the iterator reaches the end of iteration. + * + * @param it uninitialized iterator to initialize + * @param e initial position of the iterator. NULL for end of iteration. + * @param dir direction of iteration. Must be 1 (forward) or -1 (backward). + */ +static void LinkedList3Iterator_Init (LinkedList3Iterator *it, LinkedList3Node *e, int dir); + +/** + * Frees a linked list iterator. + * + * @param it iterator to free + */ +static void LinkedList3Iterator_Free (LinkedList3Iterator *it); + +/** + * Moves the iterator one node forward or backward (depending on its direction), or, + * if it's at the last or first node (depending on the direction), it reaches + * the end of iteration, or, if it's at the end of iteration, it remains there. + * Returns the the previous position. + * + * @param it the iterator + * @return node on the position of iterator before it was (possibly) moved, or NULL + * if it was at the end of iteration + */ +static LinkedList3Node * LinkedList3Iterator_Next (LinkedList3Iterator *it); + +void LinkedList3Node_InitLonely (LinkedList3Node *node) +{ + node->p = NULL; + node->n = NULL; + node->it = NULL; +} + +void LinkedList3Node_InitAfter (LinkedList3Node *node, LinkedList3Node *ref) +{ + ASSERT(ref) + + node->p = ref; + node->n = ref->n; + ref->n = node; + if (node->n) { + node->n->p = node; + } + node->it = NULL; +} + +void LinkedList3Node_InitBefore (LinkedList3Node *node, LinkedList3Node *ref) +{ + ASSERT(ref) + + node->n = ref; + node->p = ref->p; + ref->p = node; + if (node->p) { + node->p->n = node; + } + node->it = NULL; +} + +void LinkedList3Node_Free (LinkedList3Node *node) +{ + // jump iterators + while (node->it) { + LinkedList3Iterator_Next(node->it); + } + + if (node->p) { + node->p->n = node->n; + } + if (node->n) { + node->n->p = node->p; + } +} + +int LinkedList3Node_IsLonely (LinkedList3Node *node) +{ + return (!node->p && !node->n); +} + +LinkedList3Node * LinkedList3Node_PrevOrNext (LinkedList3Node *node) +{ + if (node->p) { + return node->p; + } + if (node->n) { + return node->n; + } + return NULL; +} + +LinkedList3Node * LinkedList3Node_NextOrPrev (LinkedList3Node *node) +{ + if (node->n) { + return node->n; + } + if (node->p) { + return node->p; + } + return NULL; +} + +LinkedList3Node * LinkedList3Node_Prev (LinkedList3Node *node) +{ + return node->p; +} + +LinkedList3Node * LinkedList3Node_Next (LinkedList3Node *node) +{ + return node->n; +} + +LinkedList3Node * LinkedList3Node_First (LinkedList3Node *node) +{ + while (node->p) { + node = node->p; + } + + return node; +} + +LinkedList3Node * LinkedList3Node_Last (LinkedList3Node *node) +{ + while (node->n) { + node = node->n; + } + + return node; +} + +void LinkedList3Iterator_Init (LinkedList3Iterator *it, LinkedList3Node *e, int dir) +{ + ASSERT(dir == -1 || dir == 1) + + it->dir = dir; + it->e = e; + + if (e) { + // link into node's iterator list + it->pi = NULL; + it->ni = e->it; + if (e->it) { + e->it->pi = it; + } + e->it = it; + } +} + +void LinkedList3Iterator_Free (LinkedList3Iterator *it) +{ + if (it->e) { + // remove from node's iterator list + if (it->ni) { + it->ni->pi = it->pi; + } + if (it->pi) { + it->pi->ni = it->ni; + } else { + it->e->it = it->ni; + } + } +} + +LinkedList3Node * LinkedList3Iterator_Next (LinkedList3Iterator *it) +{ + // remember original entry + LinkedList3Node *orig = it->e; + + // jump to next entry + if (it->e) { + // get next entry + LinkedList3Node *next = NULL; // to remove warning + switch (it->dir) { + case 1: + next = it->e->n; + break; + case -1: + next = it->e->p; + break; + default: + ASSERT(0); + } + // destroy interator + LinkedList3Iterator_Free(it); + // re-initialize at next entry + LinkedList3Iterator_Init(it, next, it->dir); + } + + // return original entry + return orig; +} + +#endif diff --git a/external/badvpn_dns/structure/SAvl.h b/external/badvpn_dns/structure/SAvl.h new file mode 100644 index 00000000..6ea13996 --- /dev/null +++ b/external/badvpn_dns/structure/SAvl.h @@ -0,0 +1,40 @@ +/** + * @file SAvl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SAVL_H +#define BADVPN_SAVL_H + +#include +#include + +#include +#include +#include + +#endif diff --git a/external/badvpn_dns/structure/SAvl_decl.h b/external/badvpn_dns/structure/SAvl_decl.h new file mode 100644 index 00000000..8a13e497 --- /dev/null +++ b/external/badvpn_dns/structure/SAvl_decl.h @@ -0,0 +1,73 @@ +/** + * @file SAvl_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +typedef SAvlEntry *SAvl__TreeLink; + +#include "SAvl_tree.h" +#include + +typedef struct { + SAvl__Tree tree; +} SAvl; + +typedef struct { + SAvlEntry *child[2]; + SAvlEntry *parent; + int8_t balance; +#if SAVL_PARAM_FEATURE_COUNTS + SAvlCount count; +#endif +} SAvlNode; + +static void SAvl_Init (SAvl *o); +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing); +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry); +#if !SAVL_PARAM_FEATURE_NOKEYS +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key); +#endif +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg); +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static int SAvl_IsEmpty (const SAvl *o); +static void SAvl_Verify (const SAvl *o, SAvlArg arg); +#if SAVL_PARAM_FEATURE_COUNTS +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg); +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry); +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index); +#endif + +#include "SAvl_footer.h" diff --git a/external/badvpn_dns/structure/SAvl_footer.h b/external/badvpn_dns/structure/SAvl_footer.h new file mode 100644 index 00000000..ad90e404 --- /dev/null +++ b/external/badvpn_dns/structure/SAvl_footer.h @@ -0,0 +1,89 @@ +/** + * @file SAvl_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SAVL_PARAM_NAME +#undef SAVL_PARAM_FEATURE_COUNTS +#undef SAVL_PARAM_FEATURE_NOKEYS +#undef SAVL_PARAM_TYPE_ENTRY +#undef SAVL_PARAM_TYPE_KEY +#undef SAVL_PARAM_TYPE_ARG +#undef SAVL_PARAM_TYPE_COUNT +#undef SAVL_PARAM_VALUE_COUNT_MAX +#undef SAVL_PARAM_FUN_COMPARE_ENTRIES +#undef SAVL_PARAM_FUN_COMPARE_KEY_ENTRY +#undef SAVL_PARAM_MEMBER_NODE + +#undef SAvl +#undef SAvlEntry +#undef SAvlArg +#undef SAvlKey +#undef SAvlCount +#undef SAvlNode + +#undef SAvl_Init +#undef SAvl_Insert +#undef SAvl_Remove +#undef SAvl_Lookup +#undef SAvl_LookupExact +#undef SAvl_GetFirstGreater +#undef SAvl_GetLastLesser +#undef SAvl_GetFirstGreaterEqual +#undef SAvl_GetLastLesserEqual +#undef SAvl_GetFirst +#undef SAvl_GetLast +#undef SAvl_GetNext +#undef SAvl_GetPrev +#undef SAvl_IsEmpty +#undef SAvl_Verify +#undef SAvl_Count +#undef SAvl_IndexOf +#undef SAvl_GetAt + +#undef SAvl__Tree +#undef SAvl__TreeRef +#undef SAvl__TreeLink +#undef SAvl__TreeDeref +#undef SAvl__Tree_Init +#undef SAvl__Tree_Insert +#undef SAvl__Tree_Remove +#undef SAvl__Tree_Lookup +#undef SAvl__Tree_LookupExact +#undef SAvl__Tree_GetFirstGreater +#undef SAvl__Tree_GetLastLesser +#undef SAvl__Tree_GetFirstGreaterEqual +#undef SAvl__Tree_GetLastLesserEqual +#undef SAvl__Tree_GetFirst +#undef SAvl__Tree_GetLast +#undef SAvl__Tree_GetNext +#undef SAvl__Tree_GetPrev +#undef SAvl__Tree_IsEmpty +#undef SAvl__Tree_Verify +#undef SAvl__Tree_Count +#undef SAvl__Tree_IndexOf +#undef SAvl__Tree_GetAt diff --git a/external/badvpn_dns/structure/SAvl_header.h b/external/badvpn_dns/structure/SAvl_header.h new file mode 100644 index 00000000..5d7d9b15 --- /dev/null +++ b/external/badvpn_dns/structure/SAvl_header.h @@ -0,0 +1,93 @@ +/** + * @file SAvl_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs. All types must be typedef names unless stated otherwise. +// SAVL_PARAM_NAME - name of this data structure +// SAVL_PARAM_FEATURE_COUNTS - whether to keep count information (0 or 1) +// SAVL_PARAM_FEATURE_NOKEYS - define to 1 if there is no need for a lookup operation +// SAVL_PARAM_TYPE_ENTRY - type of entry. This can also be a struct (e.g.: struct Foo). +// SAVL_PARAM_TYPE_KEY - type of key (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_TYPE_ARG - type of argument pass through to callbacks +// SAVL_PARAM_TYPE_COUNT - type of count (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_VALUE_COUNT_MAX - maximum value of count (of type SAVL_PARAM_TYPE_COUNT) (only if SAVL_PARAM_FEATURE_COUNTS) +// SAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) - compare two entries; returns -1/0/1 +// SAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) - compare key and entry; returns -1/0/1 (ignored if SAVL_PARAM_FEATURE_NOKEYS) +// SAVL_PARAM_MEMBER_NODE - node member in entry + +// types +#define SAvl SAVL_PARAM_NAME +#define SAvlEntry SAVL_PARAM_TYPE_ENTRY +#define SAvlArg SAVL_PARAM_TYPE_ARG +#define SAvlKey SAVL_PARAM_TYPE_KEY +#define SAvlCount SAVL_PARAM_TYPE_COUNT +#define SAvlNode MERGE(SAvl, Node) + +// public functions +#define SAvl_Init MERGE(SAvl, _Init) +#define SAvl_Insert MERGE(SAvl, _Insert) +#define SAvl_Remove MERGE(SAvl, _Remove) +#define SAvl_Lookup MERGE(SAvl, _Lookup) +#define SAvl_LookupExact MERGE(SAvl, _LookupExact) +#define SAvl_GetFirstGreater MERGE(SAvl, _GetFirstGreater) +#define SAvl_GetLastLesser MERGE(SAvl, _GetLastLesser) +#define SAvl_GetFirstGreaterEqual MERGE(SAvl, _GetFirstGreaterEqual) +#define SAvl_GetLastLesserEqual MERGE(SAvl, _GetLastLesserEqual) +#define SAvl_GetFirst MERGE(SAvl, _GetFirst) +#define SAvl_GetLast MERGE(SAvl, _GetLast) +#define SAvl_GetNext MERGE(SAvl, _GetNext) +#define SAvl_GetPrev MERGE(SAvl, _GetPrev) +#define SAvl_IsEmpty MERGE(SAvl, _IsEmpty) +#define SAvl_Verify MERGE(SAvl, _Verify) +#define SAvl_Count MERGE(SAvl, _Count) +#define SAvl_IndexOf MERGE(SAvl, _IndexOf) +#define SAvl_GetAt MERGE(SAvl, _GetAt) + +// internal stuff +#define SAvl__Tree MERGE(SAvl, __Tree) +#define SAvl__TreeRef MERGE(SAvl, __TreeRef) +#define SAvl__TreeLink MERGE(SAvl, __TreeLink) +#define SAvl__TreeDeref MERGE(SAvl, __TreeDeref) +#define SAvl__Tree_Init MERGE(SAvl, __Tree_Init) +#define SAvl__Tree_Insert MERGE(SAvl, __Tree_Insert) +#define SAvl__Tree_Remove MERGE(SAvl, __Tree_Remove) +#define SAvl__Tree_Lookup MERGE(SAvl, __Tree_Lookup) +#define SAvl__Tree_LookupExact MERGE(SAvl, __Tree_LookupExact) +#define SAvl__Tree_GetFirstGreater MERGE(SAvl, __Tree_GetFirstGreater) +#define SAvl__Tree_GetLastLesser MERGE(SAvl, __Tree_GetLastLesser) +#define SAvl__Tree_GetFirstGreaterEqual MERGE(SAvl, __Tree_GetFirstGreaterEqual) +#define SAvl__Tree_GetLastLesserEqual MERGE(SAvl, __Tree_GetLastLesserEqual) +#define SAvl__Tree_GetFirst MERGE(SAvl, __Tree_GetFirst) +#define SAvl__Tree_GetLast MERGE(SAvl, __Tree_GetLast) +#define SAvl__Tree_GetNext MERGE(SAvl, __Tree_GetNext) +#define SAvl__Tree_GetPrev MERGE(SAvl, __Tree_GetPrev) +#define SAvl__Tree_IsEmpty MERGE(SAvl, __Tree_IsEmpty) +#define SAvl__Tree_Verify MERGE(SAvl, __Tree_Verify) +#define SAvl__Tree_Count MERGE(SAvl, __Tree_Count) +#define SAvl__Tree_IndexOf MERGE(SAvl, __Tree_IndexOf) +#define SAvl__Tree_GetAt MERGE(SAvl, __Tree_GetAt) diff --git a/external/badvpn_dns/structure/SAvl_impl.h b/external/badvpn_dns/structure/SAvl_impl.h new file mode 100644 index 00000000..0d3973ae --- /dev/null +++ b/external/badvpn_dns/structure/SAvl_impl.h @@ -0,0 +1,164 @@ +/** + * @file SAvl_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SAvl_header.h" + +#include "SAvl_tree.h" +#include + +static void SAvl_Init (SAvl *o) +{ + SAvl__Tree_Init(&o->tree); +} + +static int SAvl_Insert (SAvl *o, SAvlArg arg, SAvlEntry *entry, SAvlEntry **out_existing) +{ + ASSERT(entry) +#if SAVL_PARAM_FEATURE_COUNTS + ASSERT(SAvl_Count(o, arg) < SAVL_PARAM_VALUE_COUNT_MAX) +#endif + + SAvl__TreeRef out_ref; + int res = SAvl__Tree_Insert(&o->tree, arg, SAvl__TreeDeref(arg, entry), &out_ref); + + if (out_existing) { + *out_existing = out_ref.link; + } + + return res; +} + +static void SAvl_Remove (SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__Tree_Remove(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +#if !SAVL_PARAM_FEATURE_NOKEYS + +static SAvlEntry * SAvl_Lookup (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_Lookup(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_LookupExact (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_LookupExact(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreater (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreater(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesser (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesser(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetFirstGreaterEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirstGreaterEqual(&o->tree, arg, key); + return ref.link; +} + +static SAvlEntry * SAvl_GetLastLesserEqual (const SAvl *o, SAvlArg arg, SAvlKey key) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLastLesserEqual(&o->tree, arg, key); + return ref.link; +} + +#endif + +static SAvlEntry * SAvl_GetFirst (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetFirst(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetLast (const SAvl *o, SAvlArg arg) +{ + SAvl__TreeRef ref = SAvl__Tree_GetLast(&o->tree, arg); + return ref.link; +} + +static SAvlEntry * SAvl_GetNext (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetNext(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static SAvlEntry * SAvl_GetPrev (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + SAvl__TreeRef ref = SAvl__Tree_GetPrev(&o->tree, arg, SAvl__TreeDeref(arg, entry)); + return ref.link; +} + +static int SAvl_IsEmpty (const SAvl *o) +{ + return SAvl__Tree_IsEmpty(&o->tree); +} + +static void SAvl_Verify (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Verify(&o->tree, arg); +} + +#if SAVL_PARAM_FEATURE_COUNTS + +static SAvlCount SAvl_Count (const SAvl *o, SAvlArg arg) +{ + return SAvl__Tree_Count(&o->tree, arg); +} + +static SAvlCount SAvl_IndexOf (const SAvl *o, SAvlArg arg, SAvlEntry *entry) +{ + ASSERT(entry) + + return SAvl__Tree_IndexOf(&o->tree, arg, SAvl__TreeDeref(arg, entry)); +} + +static SAvlEntry * SAvl_GetAt (const SAvl *o, SAvlArg arg, SAvlCount index) +{ + SAvl__TreeRef ref = SAvl__Tree_GetAt(&o->tree, arg, index); + return ref.link; +} + +#endif + +#include "SAvl_footer.h" diff --git a/external/badvpn_dns/structure/SAvl_tree.h b/external/badvpn_dns/structure/SAvl_tree.h new file mode 100644 index 00000000..5e5b18bb --- /dev/null +++ b/external/badvpn_dns/structure/SAvl_tree.h @@ -0,0 +1,18 @@ +#define CAVL_PARAM_NAME SAvl__Tree +#define CAVL_PARAM_FEATURE_COUNTS SAVL_PARAM_FEATURE_COUNTS +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS SAVL_PARAM_FEATURE_NOKEYS +#define CAVL_PARAM_TYPE_ENTRY SAvlEntry +#define CAVL_PARAM_TYPE_LINK SAvl__TreeLink +#define CAVL_PARAM_TYPE_KEY SAvlKey +#define CAVL_PARAM_TYPE_ARG SAvlArg +#define CAVL_PARAM_TYPE_COUNT SAvlCount +#define CAVL_PARAM_VALUE_COUNT_MAX SAVL_PARAM_VALUE_COUNT_MAX +#define CAVL_PARAM_VALUE_NULL ((SAvl__TreeLink)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) SAVL_PARAM_FUN_COMPARE_ENTRIES((arg), (entry1).link, (entry2).link) +#define CAVL_PARAM_FUN_COMPARE_KEY_ENTRY(arg, key1, entry2) SAVL_PARAM_FUN_COMPARE_KEY_ENTRY((arg), (key1), (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD SAVL_PARAM_MEMBER_NODE . child +#define CAVL_PARAM_MEMBER_BALANCE SAVL_PARAM_MEMBER_NODE . balance +#define CAVL_PARAM_MEMBER_PARENT SAVL_PARAM_MEMBER_NODE . parent +#define CAVL_PARAM_MEMBER_COUNT SAVL_PARAM_MEMBER_NODE . count diff --git a/external/badvpn_dns/structure/SLinkedList.h b/external/badvpn_dns/structure/SLinkedList.h new file mode 100644 index 00000000..1edb0166 --- /dev/null +++ b/external/badvpn_dns/structure/SLinkedList.h @@ -0,0 +1,38 @@ +/** + * @file SLinkedList.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SLINKEDLIST_H +#define BADVPN_SLINKEDLIST_H + +#include + +#include +#include + +#endif diff --git a/external/badvpn_dns/structure/SLinkedList_decl.h b/external/badvpn_dns/structure/SLinkedList_decl.h new file mode 100644 index 00000000..4a0ade48 --- /dev/null +++ b/external/badvpn_dns/structure/SLinkedList_decl.h @@ -0,0 +1,67 @@ +/** + * @file SLinkedList_decl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +typedef struct { + SLinkedListEntry *first; +#if SLINKEDLIST_PARAM_FEATURE_LAST + SLinkedListEntry *last; +#endif +} SLinkedList; + +typedef struct { + SLinkedListEntry *prev; + SLinkedListEntry *next; +} SLinkedListNode; + +static void SLinkedListMarkRemoved (SLinkedListEntry *entry); +static int SLinkedListIsRemoved (SLinkedListEntry *entry); + +static void SLinkedList_Init (SLinkedList *o); +static SLinkedListEntry * SLinkedList_Next (SLinkedList *o, SLinkedListEntry *entry); +static SLinkedListEntry * SLinkedList_Prev (SLinkedList *o, SLinkedListEntry *entry); +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry); +#endif +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry); +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry); +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry); +static void SLinkedList_RemoveFirst (SLinkedList *o); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_RemoveLast (SLinkedList *o); +#endif +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o); +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o); +#endif +static int SLinkedList_IsEmpty (const SLinkedList *o); + +#include "SLinkedList_footer.h" diff --git a/external/badvpn_dns/structure/SLinkedList_footer.h b/external/badvpn_dns/structure/SLinkedList_footer.h new file mode 100644 index 00000000..10ab1e32 --- /dev/null +++ b/external/badvpn_dns/structure/SLinkedList_footer.h @@ -0,0 +1,57 @@ +/** + * @file SLinkedList_footer.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#undef SLINKEDLIST_PARAM_NAME +#undef SLINKEDLIST_PARAM_FEATURE_LAST +#undef SLINKEDLIST_PARAM_TYPE_ENTRY +#undef SLINKEDLIST_PARAM_MEMBER_NODE + +#undef SLinkedList +#undef SLinkedListEntry +#undef SLinkedListNode + +#undef SLinkedListMarkRemoved +#undef SLinkedListIsRemoved + +#undef SLinkedList_Init +#undef SLinkedList_Next +#undef SLinkedList_Prev +#undef SLinkedList_Prepend +#undef SLinkedList_Append +#undef SLinkedList_InsertBefore +#undef SLinkedList_InsertAfter +#undef SLinkedList_Remove +#undef SLinkedList_RemoveFirst +#undef SLinkedList_RemoveLast +#undef SLinkedList_First +#undef SLinkedList_Last +#undef SLinkedList_IsEmpty + +#undef SLinkedList_prev +#undef SLinkedList_next diff --git a/external/badvpn_dns/structure/SLinkedList_header.h b/external/badvpn_dns/structure/SLinkedList_header.h new file mode 100644 index 00000000..4a0fd54a --- /dev/null +++ b/external/badvpn_dns/structure/SLinkedList_header.h @@ -0,0 +1,62 @@ +/** + * @file SLinkedList_header.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Preprocessor inputs: +// SLINKEDLIST_PARAM_NAME - name of this data structure +// SLINKEDLIST_PARAM_FEATURE_LAST - whether to keep track of the last entry in the list (0/1) +// SLINKEDLIST_PARAM_TYPE_ENTRY - type of entry +// SLINKEDLIST_PARAM_MEMBER_NODE - name of the node member in entry + +// types +#define SLinkedList SLINKEDLIST_PARAM_NAME +#define SLinkedListEntry SLINKEDLIST_PARAM_TYPE_ENTRY +#define SLinkedListNode MERGE(SLinkedList, Node) + +// non-object public functions +#define SLinkedListMarkRemoved MERGE(SLinkedList, MarkRemoved) +#define SLinkedListIsRemoved MERGE(SLinkedList, IsRemoved) + +// public functions +#define SLinkedList_Init MERGE(SLinkedList, _Init) +#define SLinkedList_Next MERGE(SLinkedList, _Next) +#define SLinkedList_Prev MERGE(SLinkedList, _Prev) +#define SLinkedList_Prepend MERGE(SLinkedList, _Prepend) +#define SLinkedList_Append MERGE(SLinkedList, _Append) +#define SLinkedList_InsertBefore MERGE(SLinkedList, _InsertBefore) +#define SLinkedList_InsertAfter MERGE(SLinkedList, _InsertAfter) +#define SLinkedList_Remove MERGE(SLinkedList, _Remove) +#define SLinkedList_RemoveFirst MERGE(SLinkedList, _RemoveFirst) +#define SLinkedList_RemoveLast MERGE(SLinkedList, _RemoveLast) +#define SLinkedList_First MERGE(SLinkedList, _First) +#define SLinkedList_Last MERGE(SLinkedList, _Last) +#define SLinkedList_IsEmpty MERGE(SLinkedList, _IsEmpty) + +// private things +#define SLinkedList_prev(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . prev) +#define SLinkedList_next(entry) ((entry)->SLINKEDLIST_PARAM_MEMBER_NODE . next) diff --git a/external/badvpn_dns/structure/SLinkedList_impl.h b/external/badvpn_dns/structure/SLinkedList_impl.h new file mode 100644 index 00000000..1bbce9a5 --- /dev/null +++ b/external/badvpn_dns/structure/SLinkedList_impl.h @@ -0,0 +1,182 @@ +/** + * @file SLinkedList_impl.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SLinkedList_header.h" + +static void SLinkedListMarkRemoved (SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_next(entry) = entry; +} + +static int SLinkedListIsRemoved (SLinkedListEntry *entry) +{ + ASSERT(entry) + + return (entry == SLinkedList_next(entry)); +} + +static void SLinkedList_Init (SLinkedList *o) +{ + o->first = NULL; +} + +static SLinkedListEntry * SLinkedList_Next (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + return SLinkedList_next(entry); +} + +static SLinkedListEntry * SLinkedList_Prev (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + return (entry == o->first) ? NULL : SLinkedList_prev(entry); +} + +static void SLinkedList_Prepend (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_next(entry) = o->first; + if (o->first) { + SLinkedList_prev(o->first) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + o->first = entry; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_Append (SLinkedList *o, SLinkedListEntry *entry) +{ + ASSERT(entry) + + SLinkedList_next(entry) = NULL; + if (o->first) { + SLinkedList_prev(entry) = o->last; + SLinkedList_next(o->last) = entry; + } else { + o->first = entry; + } + o->last = entry; +} +#endif + +static void SLinkedList_InsertBefore (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *before_entry) +{ + ASSERT(entry) + ASSERT(before_entry) + + SLinkedList_next(entry) = before_entry; + if (before_entry != o->first) { + SLinkedList_prev(entry) = SLinkedList_prev(before_entry); + SLinkedList_next(SLinkedList_prev(before_entry)) = entry; + } else { + o->first = entry; + } + SLinkedList_prev(before_entry) = entry; +} + +static void SLinkedList_InsertAfter (SLinkedList *o, SLinkedListEntry *entry, SLinkedListEntry *after_entry) +{ + ASSERT(entry) + ASSERT(after_entry) + + SLinkedList_next(entry) = SLinkedList_next(after_entry); + SLinkedList_prev(entry) = after_entry; + if (SLinkedList_next(after_entry)) { + SLinkedList_prev(SLinkedList_next(after_entry)) = entry; + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = entry; +#endif + } + SLinkedList_next(after_entry) = entry; +} + +static void SLinkedList_Remove (SLinkedList *o, SLinkedListEntry *entry) +{ + if (entry != o->first) { + SLinkedList_next(SLinkedList_prev(entry)) = SLinkedList_next(entry); + if (SLinkedList_next(entry)) { + SLinkedList_prev(SLinkedList_next(entry)) = SLinkedList_prev(entry); + } else { +#if SLINKEDLIST_PARAM_FEATURE_LAST + o->last = SLinkedList_prev(entry); +#endif + } + } else { + o->first = SLinkedList_next(entry); + } +} + +static void SLinkedList_RemoveFirst (SLinkedList *o) +{ + ASSERT(o->first) + + o->first = SLinkedList_next(o->first); +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static void SLinkedList_RemoveLast (SLinkedList *o) +{ + ASSERT(o->first) + + if (o->last == o->first) { + o->first = NULL; + } else { + SLinkedList_next(SLinkedList_prev(o->last)) = NULL; + o->last = SLinkedList_prev(o->last); + } +} +#endif + +static SLinkedListEntry * SLinkedList_First (const SLinkedList *o) +{ + return o->first; +} + +#if SLINKEDLIST_PARAM_FEATURE_LAST +static SLinkedListEntry * SLinkedList_Last (const SLinkedList *o) +{ + return (!o->first ? NULL : o->last); +} +#endif + +static int SLinkedList_IsEmpty (const SLinkedList *o) +{ + return !o->first; +} + +#include "SLinkedList_footer.h" diff --git a/external/badvpn_dns/system/BAddr.h b/external/badvpn_dns/system/BAddr.h new file mode 100644 index 00000000..a5f3f10b --- /dev/null +++ b/external/badvpn_dns/system/BAddr.h @@ -0,0 +1,808 @@ +/** + * @file BAddr.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Network address abstractions. + */ + +#ifndef BADVPN_SYSTEM_BADDR_H +#define BADVPN_SYSTEM_BADDR_H + +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define BADDR_TYPE_NONE 0 +#define BADDR_TYPE_IPV4 1 +#define BADDR_TYPE_IPV6 2 +#ifdef BADVPN_LINUX + #define BADDR_TYPE_PACKET 5 +#endif + +#define BADDR_MAX_ADDR_LEN 128 + +#define BIPADDR_MAX_PRINT_LEN 40 +#define BADDR_MAX_PRINT_LEN 120 + +#define BADDR_PACKET_HEADER_TYPE_ETHERNET 1 + +#define BADDR_PACKET_PACKET_TYPE_HOST 1 +#define BADDR_PACKET_PACKET_TYPE_BROADCAST 2 +#define BADDR_PACKET_PACKET_TYPE_MULTICAST 3 +#define BADDR_PACKET_PACKET_TYPE_OTHERHOST 4 +#define BADDR_PACKET_PACKET_TYPE_OUTGOING 5 + +typedef struct { + int type; + union { + uint32_t ipv4; + uint8_t ipv6[16]; + }; +} BIPAddr; + +static void BIPAddr_InitInvalid (BIPAddr *addr); + +static void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip); + +static void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip); + +static void BIPAddr_Assert (BIPAddr *addr); + +static int BIPAddr_IsInvalid (BIPAddr *addr); + +static int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) WARN_UNUSED; + +static int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2); + +/** + * Converts an IP address to human readable form. + * + * @param addr IP address to convert + * @param out destination buffer. Must be at least BIPADDR_MAX_PRINT_LEN characters long. + */ +static void BIPAddr_Print (BIPAddr *addr, char *out); + +/** + * Socket address - IP address and transport protocol port number + */ +typedef struct { + int type; + union { + struct { + uint32_t ip; + uint16_t port; + } ipv4; + struct { + uint8_t ip[16]; + uint16_t port; + } ipv6; + struct { + uint16_t phys_proto; + int interface_index; + int header_type; + int packet_type; + uint8_t phys_addr[8]; + } packet; + }; +} BAddr; + +/** + * Makes an invalid address. + */ +static BAddr BAddr_MakeNone (void); + +/** + * Makes an IPv4 address. + * + * @param ip IP address in network byte order + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port); + +/** + * Makes an IPv6 address. + * + * @param ip IP address (16 bytes) + * @param port port number in network byte order + */ +static BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port); + +/** + * Makes an address from a BIPAddr and port number. + * + * @param ipaddr the BIPAddr + * @param port port number in network byte order + */ +static BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port); + +/** + * Deprecated, use BAddr_MakeNone. + */ +static void BAddr_InitNone (BAddr *addr); + +/** + * Deprecated, use BAddr_MakeIPv4. + */ +static void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeIPv6. + */ +static void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port); + +/** + * Deprecated, use BAddr_MakeFromIpaddrAndPort. + */ +static void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port); + +/** + * Initializes a packet socket (data link layer) address. + * Only Ethernet addresses are supported. + * + * @param addr the object + * @param phys_proto identifier for the upper protocol, network byte order (EtherType) + * @param interface_index network interface index + * @param header_type data link layer header type. Must be BADDR_PACKET_HEADER_TYPE_ETHERNET. + * @param packet_type the manner in which packets are sent/received. Must be one of + * BADDR_PACKET_PACKET_TYPE_HOST, BADDR_PACKET_PACKET_TYPE_BROADCAST, + * BADDR_PACKET_PACKET_TYPE_MULTICAST, BADDR_PACKET_PACKET_TYPE_OTHERHOST, + * BADDR_PACKET_PACKET_TYPE_OUTGOING. + * @param phys_addr data link layer address (MAC address) + */ +static void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr); + +/** + * Does nothing. + * + * @param addr the object + */ +static void BAddr_Assert (BAddr *addr); + +/** + * Determines whether the address is an invalid address. + * + * @param addr the object + * @return 1 if invalid, 0 if invalid + **/ +static int BAddr_IsInvalid (BAddr *addr); + +/** + * Returns the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @return port number, in network byte order + */ +static uint16_t BAddr_GetPort (BAddr *addr); + +/** + * Returns the IP address in the address. + * + * @param addr the object + * @param ipaddr IP address will be returned here. If \a addr is not + * an IPv4 or IPv6 address, an invalid address will be + * returned. + */ +static void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr); + +/** + * Sets the port number in the address. + * + * @param addr the object + * Must be an IPv4 or IPv6 address. + * @param port port number, in network byte order + */ +static void BAddr_SetPort (BAddr *addr, uint16_t port); + +/** + * Converts an IP address to human readable form. + * + * @param addr address to convert + * @param out destination buffer. Must be at least BADDR_MAX_PRINT_LEN characters long. + */ +static void BAddr_Print (BAddr *addr, char *out); + +/** + * Resolves an address string. + * Format is "addr:port" for IPv4, "[addr]:port" for IPv6. + * addr is be a numeric address or a name. + * port is a numeric port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @param noresolve only accept numeric addresses. Avoids blocking the caller. + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) WARN_UNUSED; + +/** + * Resolves an address string. + * IPv4 input format is "a.b.c.d:p", where a.b.c.d is the IP address + * and d is the port number. + * IPv6 input format is "[addr]:p", where addr is an IPv6 addres in + * standard notation and p is the port number. + * + * @param addr output address + * @param name if not NULL, the name portion of the address will be + * stored here + * @param name_len if name is not NULL, the size of the name buffer + * @return 1 on success, 0 on parse error + */ +static int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) WARN_UNUSED; + +static int BAddr_Compare (BAddr *addr1, BAddr *addr2); + +static int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2); + +void BIPAddr_InitInvalid (BIPAddr *addr) +{ + addr->type = BADDR_TYPE_NONE; +} + +void BIPAddr_InitIPv4 (BIPAddr *addr, uint32_t ip) +{ + addr->type = BADDR_TYPE_IPV4; + addr->ipv4 = ip; +} + +void BIPAddr_InitIPv6 (BIPAddr *addr, uint8_t *ip) +{ + addr->type = BADDR_TYPE_IPV6; + memcpy(addr->ipv6, ip, 16); +} + +void BIPAddr_Assert (BIPAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + return; + default: + ASSERT(0); + } +} + +int BIPAddr_IsInvalid (BIPAddr *addr) +{ + BIPAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) +{ + int len = strlen(str); + + char *addr_start; + int addr_len; + + // determine address type + if (len >= 1 && str[0] == '[' && str[len - 1] == ']') { + addr->type = BADDR_TYPE_IPV6; + addr_start = str + 1; + addr_len = len - 2; + } else { + addr->type = BADDR_TYPE_IPV4; + addr_start = str; + addr_len = len; + } + + // copy + char addr_str[BADDR_MAX_ADDR_LEN + 1]; + if (addr_len > BADDR_MAX_ADDR_LEN) { + return 0; + } + memcpy(addr_str, addr_start, addr_len); + addr_str[addr_len] = '\0'; + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6)); + break; + case BADDR_TYPE_IPV4: + addr->ipv4 = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + break; + } + + freeaddrinfo(addrs); + + return 1; +} + +int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2) +{ + BIPAddr_Assert(addr1); + BIPAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: + return 0; + case BADDR_TYPE_IPV4: + return (addr1->ipv4 == addr2->ipv4); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6, addr2->ipv6, sizeof(addr1->ipv6))); + default: + ASSERT(0) + return 0; + } +} + +uint16_t BAddr_GetPort (BAddr *addr) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + return addr->ipv4.port; + case BADDR_TYPE_IPV6: + return addr->ipv6.port; + default: + ASSERT(0) + return 0; + } +} + +void BAddr_GetIPAddr (BAddr *addr, BIPAddr *ipaddr) +{ + BAddr_Assert(addr); + + switch (addr->type) { + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(ipaddr, addr->ipv4.ip); + return; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(ipaddr, addr->ipv6.ip); + return; + default: + BIPAddr_InitInvalid(ipaddr); + } +} + +void BAddr_SetPort (BAddr *addr, uint16_t port) +{ + BAddr_Assert(addr); + ASSERT(addr->type == BADDR_TYPE_IPV4 || addr->type == BADDR_TYPE_IPV6) + + switch (addr->type) { + case BADDR_TYPE_IPV4: + addr->ipv4.port = port; + break; + case BADDR_TYPE_IPV6: + addr->ipv6.port = port; + break; + default: + ASSERT(0); + } +} + +void BIPAddr_Print (BIPAddr *addr, char *out) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + sprintf(out, "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, + *((uint8_t *)&addr->ipv4 + 0), + *((uint8_t *)&addr->ipv4 + 1), + *((uint8_t *)&addr->ipv4 + 2), + *((uint8_t *)&addr->ipv4 + 3) + ); + break; + case BADDR_TYPE_IPV6: { + const char *ptr = (const char *)addr->ipv6; + sprintf(out, + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16":" + "%"PRIx16":%"PRIx16":%"PRIx16":%"PRIx16, + badvpn_read_be16(ptr + 0), + badvpn_read_be16(ptr + 2), + badvpn_read_be16(ptr + 4), + badvpn_read_be16(ptr + 6), + badvpn_read_be16(ptr + 8), + badvpn_read_be16(ptr + 10), + badvpn_read_be16(ptr + 12), + badvpn_read_be16(ptr + 14) + ); + } break; + default: + ASSERT(0); + } +} + +BAddr BAddr_MakeNone (void) +{ + BAddr addr; + addr.type = BADDR_TYPE_NONE; + return addr; +} + +BAddr BAddr_MakeIPv4 (uint32_t ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV4; + addr.ipv4.ip = ip; + addr.ipv4.port = port; + return addr; +} + +BAddr BAddr_MakeIPv6 (const uint8_t *ip, uint16_t port) +{ + BAddr addr; + addr.type = BADDR_TYPE_IPV6; + memcpy(addr.ipv6.ip, ip, 16); + addr.ipv6.port = port; + return addr; +} + +BAddr BAddr_MakeFromIpaddrAndPort (BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + switch (ipaddr.type) { + case BADDR_TYPE_NONE: + return BAddr_MakeNone(); + case BADDR_TYPE_IPV4: + return BAddr_MakeIPv4(ipaddr.ipv4, port); + case BADDR_TYPE_IPV6: + return BAddr_MakeIPv6(ipaddr.ipv6, port); + default: + ASSERT(0); + return BAddr_MakeNone(); + } +} + +void BAddr_InitNone (BAddr *addr) +{ + *addr = BAddr_MakeNone(); +} + +void BAddr_InitIPv4 (BAddr *addr, uint32_t ip, uint16_t port) +{ + *addr = BAddr_MakeIPv4(ip, port); +} + +void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port) +{ + *addr = BAddr_MakeIPv6(ip, port); +} + +void BAddr_InitFromIpaddrAndPort (BAddr *addr, BIPAddr ipaddr, uint16_t port) +{ + BIPAddr_Assert(&ipaddr); + + *addr = BAddr_MakeFromIpaddrAndPort(ipaddr, port); +} + +#ifdef BADVPN_LINUX + +void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr) +{ + ASSERT(header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + ASSERT(packet_type == BADDR_PACKET_PACKET_TYPE_HOST || packet_type == BADDR_PACKET_PACKET_TYPE_BROADCAST || + packet_type == BADDR_PACKET_PACKET_TYPE_MULTICAST || packet_type == BADDR_PACKET_PACKET_TYPE_OTHERHOST || + packet_type == BADDR_PACKET_PACKET_TYPE_OUTGOING) + + addr->type = BADDR_TYPE_PACKET; + addr->packet.phys_proto = phys_proto; + addr->packet.interface_index = interface_index; + addr->packet.header_type = header_type; + addr->packet.packet_type = packet_type; + memcpy(addr->packet.phys_addr, phys_addr, 6); +} + +#endif + +void BAddr_Assert (BAddr *addr) +{ + switch (addr->type) { + case BADDR_TYPE_NONE: + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + #endif + return; + default: + ASSERT(0); + } +} + +int BAddr_IsInvalid (BAddr *addr) +{ + BAddr_Assert(addr); + + return (addr->type == BADDR_TYPE_NONE); +} + +void BAddr_Print (BAddr *addr, char *out) +{ + BAddr_Assert(addr); + + BIPAddr ipaddr; + + switch (addr->type) { + case BADDR_TYPE_NONE: + sprintf(out, "(none)"); + break; + case BADDR_TYPE_IPV4: + BIPAddr_InitIPv4(&ipaddr, addr->ipv4.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv4.port)); + break; + case BADDR_TYPE_IPV6: + BIPAddr_InitIPv6(&ipaddr, addr->ipv6.ip); + BIPAddr_Print(&ipaddr, out); + sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv6.port)); + break; + #ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + sprintf(out, "proto=%"PRIu16",ifindex=%d,htype=eth,ptype=%d,addr=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8, + addr->packet.phys_proto, (int)addr->packet.interface_index, (int)addr->packet.packet_type, + addr->packet.phys_addr[0], addr->packet.phys_addr[1], addr->packet.phys_addr[2], + addr->packet.phys_addr[3], addr->packet.phys_addr[4], addr->packet.phys_addr[5]); + break; + #endif + default: + ASSERT(0); + } +} + +int BAddr_Parse2 (BAddr *addr, char *str, char *name, int name_len, int noresolve) +{ + int len = strlen(str); + if (len < 1 || len > 1000) { + return 0; + } + + int addr_start; + int addr_len; + int port_start; + int port_len; + + // leading '[' indicates an IPv6 address + if (str[0] == '[') { + addr->type = BADDR_TYPE_IPV6; + // find ']' + int i=1; + while (i < len && str[i] != ']') i++; + if (i >= len) { + return 0; + } + addr_start = 1; + addr_len = i - addr_start; + // follows ':' and port number + if (i + 1 >= len || str[i + 1] != ':') { + return 0; + } + port_start = i + 2; + port_len = len - port_start; + } + // otherwise it's an IPv4 address + else { + addr->type = BADDR_TYPE_IPV4; + // find ':' + int i=0; + while (i < len && str[i] != ':') i++; + if (i >= len) { + return 0; + } + addr_start = 0; + addr_len = i - addr_start; + port_start = i + 1; + port_len = len - port_start; + } + + // copy address and port to zero-terminated buffers + + char addr_str[128]; + if (addr_len >= sizeof(addr_str)) { + return 0; + } + memcpy(addr_str, str + addr_start, addr_len); + addr_str[addr_len] = '\0'; + + char port_str[6]; + if (port_len >= sizeof(port_str)) { + return 0; + } + memcpy(port_str, str + port_start, port_len); + port_str[port_len] = '\0'; + + // parse port + char *err; + long int conv_res = strtol(port_str, &err, 10); + if (port_str[0] == '\0' || *err != '\0') { + return 0; + } + if (conv_res < 0 || conv_res > UINT16_MAX) { + return 0; + } + uint16_t port = conv_res; + port = hton16(port); + + // initialize hints + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + switch (addr->type) { + case BADDR_TYPE_IPV6: + hints.ai_family = AF_INET6; + break; + case BADDR_TYPE_IPV4: + hints.ai_family = AF_INET; + break; + } + if (noresolve) { + hints.ai_flags |= AI_NUMERICHOST; + } + + // call getaddrinfo + struct addrinfo *addrs; + int res; + if ((res = getaddrinfo(addr_str, NULL, &hints, &addrs)) != 0) { + return 0; + } + + // set address + switch (addr->type) { + case BADDR_TYPE_IPV6: + memcpy(addr->ipv6.ip, ((struct sockaddr_in6 *)addrs->ai_addr)->sin6_addr.s6_addr, sizeof(addr->ipv6.ip)); + addr->ipv6.port = port; + break; + case BADDR_TYPE_IPV4: + addr->ipv4.ip = ((struct sockaddr_in *)addrs->ai_addr)->sin_addr.s_addr; + addr->ipv4.port = port; + break; + } + + freeaddrinfo(addrs); + + if (name) { + if (strlen(addr_str) >= name_len) { + return 0; + } + strcpy(name, addr_str); + } + + return 1; +} + +int BAddr_Parse (BAddr *addr, char *str, char *name, int name_len) +{ + return BAddr_Parse2(addr, str, name, name_len, 0); +} + +int BAddr_Compare (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + if (addr1->type != addr2->type) { + return 0; + } + + switch (addr1->type) { + case BADDR_TYPE_IPV4: + return (addr1->ipv4.ip == addr2->ipv4.ip && addr1->ipv4.port == addr2->ipv4.port); + case BADDR_TYPE_IPV6: + return (!memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)) && addr1->ipv6.port == addr2->ipv6.port); + default: + return 0; + } +} + +int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2) +{ + BAddr_Assert(addr1); + BAddr_Assert(addr2); + + int cmp = B_COMPARE(addr1->type, addr2->type); + if (cmp) { + return cmp; + } + + switch (addr1->type) { + case BADDR_TYPE_NONE: { + return 0; + } break; + case BADDR_TYPE_IPV4: { + uint32_t ip1 = ntoh32(addr1->ipv4.ip); + uint32_t ip2 = ntoh32(addr2->ipv4.ip); + cmp = B_COMPARE(ip1, ip2); + if (cmp) { + return cmp; + } + uint16_t port1 = ntoh16(addr1->ipv4.port); + uint16_t port2 = ntoh16(addr2->ipv4.port); + return B_COMPARE(port1, port2); + } break; + case BADDR_TYPE_IPV6: { + cmp = memcmp(addr1->ipv6.ip, addr2->ipv6.ip, sizeof(addr1->ipv6.ip)); + if (cmp) { + return B_COMPARE(cmp, 0); + } + uint16_t port1 = ntoh16(addr1->ipv6.port); + uint16_t port2 = ntoh16(addr2->ipv6.port); + return B_COMPARE(port1, port2); + } break; + default: { + return 0; + } break; + } +} + +#endif diff --git a/external/badvpn_dns/system/BConnection.h b/external/badvpn_dns/system/BConnection.h new file mode 100644 index 00000000..eac25ec0 --- /dev/null +++ b/external/badvpn_dns/system/BConnection.h @@ -0,0 +1,369 @@ +/** + * @file BConnection.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BCONNECTION +#define BADVPN_SYSTEM_BCONNECTION + +#include +#include +#include +#include +#include +#include + + + +/** + * Checks if the given address is supported by {@link BConnection} and related objects. + * + * @param addr address to check. Must be a proper {@link BAddr} object according to + * {@link BIPAddr_Assert}. + * @return 1 if supported, 0 if not + */ +int BConnection_AddressSupported (BAddr addr); + + + +struct BListener_s; + +/** + * Object which listens for connections on an address. + * When a connection is ready, the {@link BListener_handler} handler is called, from which + * the connection can be accepted into a new {@link BConnection} object. + */ +typedef struct BListener_s BListener; + +/** + * Handler called when a new connection is ready. + * The connection can be accepted by calling {@link BConnection_Init} with the a + * BCONNECTION_SOURCE_LISTENER 'source' argument. + * If no attempt is made to accept the connection from the job closure of this handler, + * the connection will be discarded. + * + * @param user as in {@link BListener_Init} + */ +typedef void (*BListener_handler) (void *user); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to listen on + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for listening on a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for listening + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when a connection can be accepted + * @return 1 on success, 0 on failure + */ +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BListener_Free (BListener *o); + + + +struct BConnector_s; + +/** + * Object which connects to an address. + * When the connection attempt finishes, the {@link BConnector_handler} handler is called, from which, + * if successful, the resulting connection can be moved to a new {@link BConnection} object. + */ +typedef struct BConnector_s BConnector; + +/** + * Handler called when the connection attempt finishes. + * If the connection attempt succeeded (is_error==0), the new connection can be used by calling + * {@link BConnection_Init} with a BCONNECTION_SOURCE_TYPE_CONNECTOR 'source' argument. + * This handler will be called at most once. The connector object need not be freed after it + * is called. + * + * @param user as in {@link BConnector_Init} + * @param is_error whether the connection attempt succeeded (0) or failed (1) + */ +typedef void (*BConnector_handler) (void *user, int is_error); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param addr address to connect to + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; + +#ifndef BADVPN_USE_WINAPI +/** + * Initializes the object for connecting to a Unix socket. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param socket_path socket path for connecting + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when the connection attempt finishes + * @return 1 on success, 0 on failure + */ +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; +#endif + +/** + * Frees the object. + * + * @param o the object + */ +void BConnector_Free (BConnector *o); + + + +#define BCONNECTION_SOURCE_TYPE_LISTENER 1 +#define BCONNECTION_SOURCE_TYPE_CONNECTOR 2 +#define BCONNECTION_SOURCE_TYPE_PIPE 3 + +struct BConnection_source { + int type; + union { + struct { + BListener *listener; + BAddr *out_addr; + } listener; + struct { + BConnector *connector; + } connector; +#ifndef BADVPN_USE_WINAPI + struct { + int pipefd; + } pipe; +#endif + } u; +}; + +static struct BConnection_source BConnection_source_listener (BListener *listener, BAddr *out_addr) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_LISTENER; + s.u.listener.listener = listener; + s.u.listener.out_addr = out_addr; + return s; +} + +static struct BConnection_source BConnection_source_connector (BConnector *connector) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_CONNECTOR; + s.u.connector.connector = connector; + return s; +} + +#ifndef BADVPN_USE_WINAPI +static struct BConnection_source BConnection_source_pipe (int pipefd) +{ + struct BConnection_source s; + s.type = BCONNECTION_SOURCE_TYPE_PIPE; + s.u.pipe.pipefd = pipefd; + return s; +} +#endif + +struct BConnection_s; + +/** + * Object which represents a stream connection. This is usually a TCP connection, either client + * or server, but may also be used with any file descriptor (e.g. pipe) on Unix-like systems. + * Sending and receiving is performed via {@link StreamPassInterface} and {@link StreamRecvInterface}, + * respectively. + */ +typedef struct BConnection_s BConnection; + +#define BCONNECTION_EVENT_ERROR 1 +#define BCONNECTION_EVENT_RECVCLOSED 2 + +/** + * Handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * - If event is BCONNECTION_EVENT_ERROR, the connection is no longer usable and must be freed + * from withing the job closure of this handler. No further I/O or interface initialization + * must occur. + * - If event is BCONNECTION_EVENT_RECVCLOSED, no further receive I/O or receive interface + * initialization must occur. It is guarantted that the receive interface was initialized. + * + * @param user as in {@link BConnection_Init} or {@link BConnection_SetHandlers} + * @param event what happened - BCONNECTION_EVENT_ERROR or BCONNECTION_EVENT_RECVCLOSED + */ +typedef void (*BConnection_handler) (void *user, int event); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param source specifies what the connection comes from. This argument must be created with one of the + * following macros: + * - BCONNECTION_SOURCE_LISTENER(BListener *, BAddr *) + * Accepts a connection ready on a {@link BListener} object. Must be called from the job + * closure of the listener's {@link BListener_handler}, and must be the first attempt + * for this handler invocation. The address of the client is written if the address + * argument is not NULL (theoretically an invalid address may be returned). + * - BCONNECTION_SOURCE_CONNECTOR(Bconnector *) + * Uses a connection establised with {@link BConnector}. Must be called from the job + * closure of the connector's {@link BConnector_handler}, the handler must be reporting + * successful connection, and must be the first attempt for this handler invocation. + * - BCONNECTION_SOURCE_PIPE(int) + * On Unix-like systems, uses the provided file descriptor. The file descriptor number must + * be >=0. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs or the receive end of the connection was closed + * by the remote peer. + * @return 1 on success, 0 on failure + */ +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * If the connection was created with a BCONNECTION_SOURCE_PIPE 'source' argument, the file descriptor + * will not be closed. + * + * @param o the object + */ +void BConnection_Free (BConnection *o); + +/** + * Updates the handler function. + * + * @param o the object + * @param user argument to handler + * @param handler new handler function, as in {@link BConnection_Init}. Additionally, may be NULL to + * remove the current handler. In this case, a proper handler must be set before anything + * can happen with the connection. This is used when moving the connection ownership from + * one module to another. + */ +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler); + +/** + * Sets the SO_SNDBUF socket option. + * + * @param o the object + * @param buf_size value for SO_SNDBUF option + * @return 1 on success, 0 on failure + */ +int BConnection_SetSendBuffer (BConnection *o, int buf_size); + +/** + * Initializes the send interface for the connection. + * The send interface must not be initialized. + * + * @param o the object + */ +void BConnection_SendAsync_Init (BConnection *o); + +/** + * Frees the send interface for the connection. + * The send interface must be initialized. + * If the send interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_SendAsync_Free (BConnection *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * + * @param o the object + * @return send interface + */ +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o); + +/** + * Initializes the receive interface for the connection. + * The receive interface must not be initialized. + * + * @param o the object + */ +void BConnection_RecvAsync_Init (BConnection *o); + +/** + * Frees the receive interface for the connection. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the connection is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BConnection_RecvAsync_Free (BConnection *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * + * @param o the object + * @return receive interface + */ +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o); + + + +#ifdef BADVPN_USE_WINAPI +#include "BConnection_win.h" +#else +#include "BConnection_unix.h" +#endif + +#endif diff --git a/external/badvpn_dns/system/BConnectionGeneric.h b/external/badvpn_dns/system/BConnectionGeneric.h new file mode 100644 index 00000000..94175555 --- /dev/null +++ b/external/badvpn_dns/system/BConnectionGeneric.h @@ -0,0 +1,144 @@ +/** + * @file BConnectionGeneric.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BCONNECTION_GENERIC_H +#define BADVPN_BCONNECTION_GENERIC_H + +#include + +#include +#include +#include +#include + +#define BCONNECTION_ADDR_TYPE_BADDR 1 +#define BCONNECTION_ADDR_TYPE_UNIX 2 + +struct BConnection_addr { + int type; + union { + BAddr baddr; + struct { + const char *str; + size_t len; + } unix_socket_path; + } u; +}; + +static struct BConnection_addr BConnection_addr_baddr (BAddr baddr) +{ + struct BConnection_addr addr; + addr.type = BCONNECTION_ADDR_TYPE_BADDR; + addr.u.baddr = baddr; + return addr; +} + +static struct BConnection_addr BConnection_addr_unix (const char *unix_socket_path, size_t len) +{ + struct BConnection_addr addr; + addr.type = BCONNECTION_ADDR_TYPE_UNIX; + addr.u.unix_socket_path.str = unix_socket_path; + addr.u.unix_socket_path.len = len; + return addr; +} + +static int BListener_InitGeneric (BListener *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BListener_handler handler) WARN_UNUSED; +static int BConnector_InitGeneric (BConnector *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BConnector_handler handler) WARN_UNUSED; + +static int BListener_InitGeneric (BListener *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + ASSERT(addr.type != BCONNECTION_ADDR_TYPE_UNIX || !memchr(addr.u.unix_socket_path.str, '\0', addr.u.unix_socket_path.len)) + + switch (addr.type) { + case BCONNECTION_ADDR_TYPE_BADDR: { + return BListener_Init(o, addr.u.baddr, reactor, user, handler); + } break; + + case BCONNECTION_ADDR_TYPE_UNIX: { +#ifdef BADVPN_USE_WINAPI + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "unix sockets not supported"); + return 0; +#else + char *str = b_strdup_bin(addr.u.unix_socket_path.str, addr.u.unix_socket_path.len); + if (!str) { + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "b_strdup_bin failed"); + return 0; + } + int res = BListener_InitUnix(o, str, reactor, user, handler); + free(str); + return res; +#endif + } break; + + default: + ASSERT(0); + return 0; + } +} + +static int BConnector_InitGeneric (BConnector *o, struct BConnection_addr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + ASSERT(addr.type != BCONNECTION_ADDR_TYPE_UNIX || !memchr(addr.u.unix_socket_path.str, '\0', addr.u.unix_socket_path.len)) + + switch (addr.type) { + case BCONNECTION_ADDR_TYPE_BADDR: { + return BConnector_Init(o, addr.u.baddr, reactor, user, handler); + } break; + + case BCONNECTION_ADDR_TYPE_UNIX: { +#ifdef BADVPN_USE_WINAPI + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "unix sockets not supported"); + return 0; +#else + char *str = b_strdup_bin(addr.u.unix_socket_path.str, addr.u.unix_socket_path.len); + if (!str) { + BLog_LogToChannel(BLOG_CHANNEL_BConnection, BLOG_ERROR, "b_strdup_bin failed"); + return 0; + } + int res = BConnector_InitUnix(o, str, reactor, user, handler); + free(str); + return res; +#endif + } break; + + default: + ASSERT(0); + return 0; + } +} + +#endif diff --git a/external/badvpn_dns/system/BConnection_unix.c b/external/badvpn_dns/system/BConnection_unix.c new file mode 100644 index 00000000..f6fa3564 --- /dev/null +++ b/external/badvpn_dns/system/BConnection_unix.c @@ -0,0 +1,1057 @@ +/** + * @file BConnection_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "BConnection.h" + +#include + +#define MAX_UNIX_SOCKET_PATH 200 + +#define SEND_STATE_NOT_INITED 0 +#define SEND_STATE_READY 1 +#define SEND_STATE_BUSY 2 + +#define RECV_STATE_NOT_INITED 0 +#define RECV_STATE_READY 1 +#define RECV_STATE_BUSY 2 +#define RECV_STATE_INITED_CLOSED 3 +#define RECV_STATE_NOT_INITED_CLOSED 4 + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct unix_addr { + socklen_t len; + union { + struct sockaddr_un addr; + uint8_t bytes[offsetof(struct sockaddr_un, sun_path) + MAX_UNIX_SOCKET_PATH + 1]; + } u; +}; + +static int build_unix_address (struct unix_addr *out, const char *socket_path); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void listener_fd_handler (BListener *o, int events); +static void listener_default_job_handler (BListener *o); +static void connector_fd_handler (BConnector *o, int events); +static void connector_job_handler (BConnector *o); +static void connection_report_error (BConnection *o); +static void connection_send (BConnection *o); +static void connection_recv (BConnection *o); +static void connection_fd_handler (BConnection *o, int events); +static void connection_send_job_handler (BConnection *o); +static void connection_recv_job_handler (BConnection *o); +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len); +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_len); + +static int build_unix_address (struct unix_addr *out, const char *socket_path) +{ + ASSERT(socket_path); + + if (strlen(socket_path) > MAX_UNIX_SOCKET_PATH) { + return 0; + } + + out->len = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path) + 1; + out->u.addr.sun_family = AF_UNIX; + strcpy(out->u.addr.sun_path, socket_path); + + return 1; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void listener_fd_handler (BListener *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // set default job + BPending_Set(&o->default_job); + + // call handler + o->handler(o->user); + return; +} + +static void listener_default_job_handler (BListener *o) +{ + DebugObject_Access(&o->d_obj); + + BLog(BLOG_ERROR, "discarding connection"); + + // accept + int newfd = accept(o->fd, NULL, NULL); + if (newfd < 0) { + BLog(BLOG_ERROR, "accept failed"); + return; + } + + // close new fd + if (close(newfd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +static void connector_fd_handler (BConnector *o, int events) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(!o->connected) + ASSERT(o->have_bfd) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // set have no BFileDescriptor + o->have_bfd = 0; + + // read connection result + int result; + socklen_t result_len = sizeof(result); + if (getsockopt(o->fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) { + BLog(BLOG_ERROR, "getsockopt failed"); + goto fail0; + } + ASSERT_FORCE(result_len == sizeof(result)) + + if (result != 0) { + BLog(BLOG_ERROR, "connection failed"); + goto fail0; + } + + // set connected + o->connected = 1; + +fail0: + // call handler + o->handler(o->user, !o->connected); + return; +} + +static void connector_job_handler (BConnector *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->fd >= 0) + ASSERT(o->connected) + ASSERT(!o->have_bfd) + + // call handler + o->handler(o->user, 0); + return; +} + +static void connection_report_error (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->handler) + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR)); + return; +} + +static void connection_send (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // send + int bytes = write(o->fd, o->send.busy_data, o->send.busy_data_len); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + connection_report_error(o); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->send.busy_data_len) + + // set ready + o->send.state = SEND_STATE_READY; + + // done + StreamPassInterface_Done(&o->send.iface, bytes); +} + +static void connection_recv (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + // limit + if (!o->is_hupd) { + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + } + + // recv + int bytes = read(o->fd, o->recv.busy_data, o->recv.busy_data_avail); + if (bytes < 0) { + if (!o->is_hupd && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + connection_report_error(o); + return; + } + + if (bytes == 0) { + // set recv inited closed + o->recv.state = RECV_STATE_INITED_CLOSED; + + // report recv closed + o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->recv.busy_data_avail) + + // set not busy + o->recv.state = RECV_STATE_READY; + + // done + StreamRecvInterface_Done(&o->recv.iface, bytes); +} + +static void connection_fd_handler (BConnection *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->is_hupd) + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + // if we got a HUP event, stop monitoring the file descriptor + if ((events & BREACTOR_HUP)) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + o->is_hupd = 1; + } + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.state == SEND_STATE_BUSY)) { + ASSERT(o->send.state == SEND_STATE_BUSY) + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.state == RECV_STATE_BUSY)) { + ASSERT(o->recv.state == RECV_STATE_BUSY) + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + connection_send(o); + return; + } + + if (have_recv) { + connection_recv(o); + return; + } + + if (!o->is_hupd) { + BLog(BLOG_ERROR, "fd error event"); + connection_report_error(o); + return; + } +} + +static void connection_send_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_BUSY) + + connection_send(o); + return; +} + +static void connection_recv_job_handler (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_BUSY) + + connection_recv(o); + return; +} + +static void connection_send_if_handler_send (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_READY) + ASSERT(data_len > 0) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.state = SEND_STATE_BUSY; + + connection_send(o); + return; +} + +static void connection_recv_if_handler_recv (BConnection *o, uint8_t *data, int data_avail) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_READY) + ASSERT(data_avail > 0) + + // remember data + o->recv.busy_data = data; + o->recv.busy_data_avail = data_avail; + + // set busy + o->recv.state = RECV_STATE_BUSY; + + connection_recv(o); + return; +} + +int BConnection_AddressSupported (BAddr addr) +{ + BAddr_Assert(&addr); + + return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6); +} + +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // set no unix socket path + o->unix_socket_path = NULL; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // set SO_REUSEADDR + int optval = 1; + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + BLog(BLOG_ERROR, "setsockopt(SO_REUSEADDR) failed"); + } + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail1; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +int BListener_InitUnix (BListener *o, const char *socket_path, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // copy socket path + o->unix_socket_path = b_strdup(socket_path); + if (!o->unix_socket_path) { + BLog(BLOG_ERROR, "b_strdup failed"); + goto fail0; + } + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail1; + } + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // unlink existing socket + if (unlink(o->unix_socket_path) < 0 && errno != ENOENT) { + BLog(BLOG_ERROR, "unlink existing socket failed"); + goto fail2; + } + + // bind + if (bind(o->fd, (struct sockaddr *)&addr.u.addr, addr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail2; + } + + // listen + if (listen(o->fd, BCONNECTION_LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail3; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)listener_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail3; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init default job + BPending_Init(&o->default_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_default_job_handler, o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + free(o->unix_socket_path); +fail0: + return 0; +} + +void BListener_Free (BListener *o) +{ + DebugObject_Free(&o->d_obj); + + // free default job + BPending_Free(&o->default_job); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + + // unlink unix socket + if (o->unix_socket_path) { + if (unlink(o->unix_socket_path) < 0) { + BLog(BLOG_ERROR, "unlink socket failed"); + } + } + + // free unix socket path + if (o->unix_socket_path) { + free(o->unix_socket_path); + } +} + +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, &sysaddr.addr.generic, sysaddr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +int BConnector_InitUnix (BConnector *o, const char *socket_path, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(socket_path) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // build address + struct unix_addr addr; + if (!build_unix_address(&addr, socket_path)) { + BLog(BLOG_ERROR, "build_unix_address failed"); + goto fail0; + } + + // init job + BPending_Init(&o->job, BReactor_PendingGroup(o->reactor), (BPending_handler)connector_job_handler, o); + + // init fd + if ((o->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail2; + } + + // connect fd + int res = connect(o->fd, (struct sockaddr *)&addr.u.addr, addr.len); + if (res < 0 && errno != EINPROGRESS) { + BLog(BLOG_ERROR, "connect failed"); + goto fail2; + } + + // set not connected + o->connected = 0; + + // set have no BFileDescriptor + o->have_bfd = 0; + + if (res < 0) { + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connector_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_WRITE); + + // set have BFileDescriptor + o->have_bfd = 1; + } else { + // set connected + o->connected = 1; + + // set job + BPending_Set(&o->job); + } + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail1: + BPending_Free(&o->job); +fail0: + return 0; +} + +void BConnector_Free (BConnector *o) +{ + DebugObject_Free(&o->d_obj); + + // free BFileDescriptor + if (o->have_bfd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->fd != -1) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } + + // free job + BPending_Free(&o->job); +} + +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) +{ + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + DebugObject_Access(&listener->d_obj); + ASSERT(BPending_IsSet(&listener->default_job)) + } break; + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + DebugObject_Access(&connector->d_obj); + ASSERT(connector->fd >= 0) + ASSERT(connector->connected) + ASSERT(!connector->have_bfd) + ASSERT(!BPending_IsSet(&connector->job)) + } break; + case BCONNECTION_SOURCE_TYPE_PIPE: { + ASSERT(source.u.pipe.pipefd >= 0) + } break; + default: ASSERT(0); + } + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + + // unset listener's default job + BPending_Unset(&listener->default_job); + + // accept + struct sys_addr sysaddr; + sysaddr.len = sizeof(sysaddr.addr); + if ((o->fd = accept(listener->fd, &sysaddr.addr.generic, &sysaddr.len)) < 0) { + BLog(BLOG_ERROR, "accept failed"); + goto fail0; + } + o->close_fd = 1; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // return address + if (source.u.listener.out_addr) { + addr_sys_to_socket(source.u.listener.out_addr, sysaddr); + } + } break; + + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + + // grab fd from connector + o->fd = connector->fd; + connector->fd = -1; + o->close_fd = 1; + } break; + + case BCONNECTION_SOURCE_TYPE_PIPE: { + // use user-provided fd + o->fd = source.u.pipe.pipefd; + o->close_fd = 0; + + // set non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + } break; + } + + // set not HUPd + o->is_hupd = 0; + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)connection_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BCONNECTION_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BCONNECTION_RECV_LIMIT); + + // set send and recv not inited + o->send.state = SEND_STATE_NOT_INITED; + o->recv.state = RECV_STATE_NOT_INITED; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +fail0: + return 0; +} + +void BConnection_Free (BConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + ASSERT(o->recv.state == RECV_STATE_NOT_INITED || o->recv.state == RECV_STATE_NOT_INITED_CLOSED) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + if (!o->is_hupd) { + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + } + + // close fd + if (o->close_fd) { + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + } +} + +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler) +{ + DebugObject_Access(&o->d_obj); + + // set handlers + o->user = user; + o->handler = handler; +} + +int BConnection_SetSendBuffer (BConnection *o, int buf_size) +{ + DebugObject_Access(&o->d_obj); + + if (setsockopt(o->fd, SOL_SOCKET, SO_SNDBUF, (void *)&buf_size, sizeof(buf_size)) < 0) { + BLog(BLOG_ERROR, "setsockopt failed"); + return 0; + } + + return 1; +} + +void BConnection_SendAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.state == SEND_STATE_NOT_INITED) + + // init interface + StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_send_job_handler, o); + + // set ready + o->send.state = SEND_STATE_READY; +} + +void BConnection_SendAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->send.job); + + // free interface + StreamPassInterface_Free(&o->send.iface); + + // set not inited + o->send.state = SEND_STATE_NOT_INITED; +} + +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.state == SEND_STATE_READY || o->send.state == SEND_STATE_BUSY) + + return &o->send.iface; +} + +void BConnection_RecvAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.state == RECV_STATE_NOT_INITED) + + // init interface + StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_recv_job_handler, o); + + // set ready + o->recv.state = RECV_STATE_READY; +} + +void BConnection_RecvAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + // update events + if (!o->is_hupd) { + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + } + + // free job + BPending_Free(&o->recv.job); + + // free interface + StreamRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.state = RECV_STATE_NOT_INITED; +} + +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.state == RECV_STATE_READY || o->recv.state == RECV_STATE_BUSY || o->recv.state == RECV_STATE_INITED_CLOSED) + + return &o->recv.iface; +} diff --git a/external/badvpn_dns/system/BConnection_unix.h b/external/badvpn_dns/system/BConnection_unix.h new file mode 100644 index 00000000..e134f6c7 --- /dev/null +++ b/external/badvpn_dns/system/BConnection_unix.h @@ -0,0 +1,87 @@ +/** + * @file BConnection_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BCONNECTION_SEND_LIMIT 2 +#define BCONNECTION_RECV_LIMIT 2 +#define BCONNECTION_LISTEN_BACKLOG 128 + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + char *unix_socket_path; + int fd; + BFileDescriptor bfd; + BPending default_job; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + BPending job; + int fd; + int connected; + int have_bfd; + BFileDescriptor bfd; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + int fd; + int close_fd; + int is_hupd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + StreamPassInterface iface; + BPending job; + const uint8_t *busy_data; + int busy_data_len; + int state; + } send; + struct { + BReactorLimit limit; + StreamRecvInterface iface; + BPending job; + uint8_t *busy_data; + int busy_data_avail; + int state; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/external/badvpn_dns/system/BConnection_win.c b/external/badvpn_dns/system/BConnection_win.c new file mode 100644 index 00000000..17ae7271 --- /dev/null +++ b/external/badvpn_dns/system/BConnection_win.c @@ -0,0 +1,875 @@ +/** + * @file BConnection_win.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "BConnection.h" + +#include + +#define LISTEN_BACKLOG 128 + +struct sys_addr { + int len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_any_to_sys (struct sys_addr *out, int family); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void listener_next_job_handler (BListener *o); +static void listener_olap_handler (BListener *o, int event, DWORD bytes); +static void connector_olap_handler (BConnector *o, int event, DWORD bytes); +static void connector_abort (BConnector *o); +static void connection_report_error (BConnection *o); +static void connection_abort (BConnection *o); +static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len); +static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len); +static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes); +static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes); + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_any_to_sys (struct sys_addr *out, int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = 0; + out->addr.ipv4.sin_addr.s_addr = INADDR_ANY; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = 0; + out->addr.ipv6.sin6_flowinfo = 0; + struct in6_addr any = IN6ADDR_ANY_INIT; + out->addr.ipv6.sin6_addr = any; + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void listener_next_job_handler (BListener *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->busy) + + // free ready socket + if (o->ready) { + BLog(BLOG_ERROR, "discarding connection"); + + // close new socket + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // set not ready + o->ready = 0; + } + + // create new socket + if ((o->newsock = WSASocket(o->sys_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // start accept operation + while (1) { + memset(&o->olap.olap, 0, sizeof(o->olap.olap)); + DWORD bytes; + BOOL res = o->fnAcceptEx(o->sock, o->newsock, o->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub), &bytes, &o->olap.olap); + if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "AcceptEx failed"); + continue; + } + break; + } + + // set busy + o->busy = 1; + + return; + +fail0: + return; +} + +static void listener_olap_handler (BListener *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->busy) + ASSERT(!o->ready) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->busy = 0; + + // schedule next accept + BPending_Set(&o->next_job); + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "accepting failed"); + + // close new socket + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + return; + } + + BLog(BLOG_INFO, "connection accepted"); + + // set ready + o->ready = 1; + + // call handler + o->handler(o->user); + return; +} + +static void connector_olap_handler (BConnector *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->sock != INVALID_SOCKET) + ASSERT(o->busy) + ASSERT(!o->ready) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "connection failed"); + } else { + // set ready + o->ready = 1; + } + + // call handler + o->handler(o->user, !o->ready); + return; +} + +static void connector_abort (BConnector *o) +{ + if (o->sock != INVALID_SOCKET) { + // cancel I/O + if (o->busy) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + } + + // wait for connect operation to finish + if (o->busy) { + BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL); + } + + // free olap + BReactorIOCPOverlapped_Free(&o->olap); +} + +static void connection_report_error (BConnection *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->handler) + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BCONNECTION_EVENT_ERROR)); + return; +} + +static void connection_abort (BConnection *o) +{ + ASSERT(!o->aborted) + + // cancel I/O + if ((o->recv.inited && o->recv.busy) || (o->send.inited && o->send.busy)) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for receiving to complete + if (o->recv.inited && o->recv.busy) { + BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL); + } + + // wait for sending to complete + if (o->send.inited && o->send.busy) { + BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv.olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send.olap); + + // set aborted + o->aborted = 1; +} + +static void connection_send_iface_handler_send (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(!o->send.busy) + ASSERT(data_len > 0) + + if (data_len > ULONG_MAX) { + data_len = ULONG_MAX; + } + + WSABUF buf; + buf.buf = (char *)data; + buf.len = data_len; + + memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap)); + + // send + int res = WSASend(o->sock, &buf, 1, NULL, 0, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASend failed (%d)", WSAGetLastError()); + connection_report_error(o); + return; + } + + // set busy + o->send.busy = 1; + o->send.busy_data_len = data_len; +} + +static void connection_recv_iface_handler_recv (BConnection *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(!o->recv.busy) + ASSERT(data_len > 0) + + if (data_len > ULONG_MAX) { + data_len = ULONG_MAX; + } + + WSABUF buf; + buf.buf = (char *)data; + buf.len = data_len; + + memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap)); + + // recv + DWORD flags = 0; + int res = WSARecv(o->sock, &buf, 1, NULL, &flags, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecv failed (%d)", WSAGetLastError()); + connection_report_error(o); + return; + } + + // set busy + o->recv.busy = 1; + o->recv.busy_data_len = data_len; +} + +static void connection_send_olap_handler (BConnection *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->send.busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "sending failed"); + connection_report_error(o); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->send.busy_data_len) + + // done + StreamPassInterface_Done(&o->send.iface, bytes); +} + +static void connection_recv_olap_handler (BConnection *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->recv.busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "receiving failed"); + connection_report_error(o); + return; + } + + if (bytes == 0) { + // set closed + o->recv.closed = 1; + + // report recv closed + o->handler(o->user, BCONNECTION_EVENT_RECVCLOSED); + return; + } + + ASSERT(bytes > 0) + ASSERT(bytes <= o->recv.busy_data_len) + + // done + StreamRecvInterface_Done(&o->recv.iface, bytes); +} + +int BConnection_AddressSupported (BAddr addr) +{ + BAddr_Assert(&addr); + + return (addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6); +} + +int BListener_Init (BListener *o, BAddr addr, BReactor *reactor, void *user, + BListener_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // remember family + o->sys_family = sysaddr.addr.generic.sa_family; + + // init socket + if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // bind + if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // listen + if (listen(o->sock, LISTEN_BACKLOG) < 0) { + BLog(BLOG_ERROR, "listen failed"); + goto fail1; + } + + DWORD out_bytes; + + // obtain AcceptEx + GUID guid1 = WSAID_ACCEPTEX; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid1, sizeof(guid1), &o->fnAcceptEx, sizeof(o->fnAcceptEx), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to obtain AcceptEx"); + goto fail1; + } + + // obtain GetAcceptExSockaddrs + GUID guid2 = WSAID_GETACCEPTEXSOCKADDRS; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid2, sizeof(guid2), &o->fnGetAcceptExSockaddrs, sizeof(o->fnGetAcceptExSockaddrs), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to obtain GetAcceptExSockaddrs"); + goto fail1; + } + + // init olap + BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)listener_olap_handler); + + // init next job + BPending_Init(&o->next_job, BReactor_PendingGroup(o->reactor), (BPending_handler)listener_next_job_handler, o); + + // set not busy + o->busy = 0; + + // set not ready + o->ready = 0; + + // set next job + BPending_Set(&o->next_job); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BListener_Free (BListener *o) +{ + DebugObject_Free(&o->d_obj); + + // cancel I/O + if (o->busy) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for accept operation to finish + if (o->busy) { + BReactorIOCPOverlapped_Wait(&o->olap, NULL, NULL); + } + + // close new socket + if (o->busy || o->ready) { + if (closesocket(o->newsock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + } + + // free next job + BPending_Free(&o->next_job); + + // free olap + BReactorIOCPOverlapped_Free(&o->olap); +} + +int BConnector_Init (BConnector *o, BAddr addr, BReactor *reactor, void *user, + BConnector_handler handler) +{ + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // check address + if (!BConnection_AddressSupported(addr)) { + BLog(BLOG_ERROR, "address not supported"); + goto fail0; + } + + // convert address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // create local any address + struct sys_addr local_sysaddr; + addr_any_to_sys(&local_sysaddr, addr.type); + + // init socket + if ((o->sock = WSASocket(sysaddr.addr.generic.sa_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // bind socket + if (bind(o->sock, &local_sysaddr.addr.generic, local_sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + goto fail1; + } + + // obtain ConnectEx + GUID guid = WSAID_CONNECTEX; + DWORD out_bytes; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &o->fnConnectEx, sizeof(o->fnConnectEx), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "faild to get ConnectEx"); + goto fail1; + } + + // init olap + BReactorIOCPOverlapped_Init(&o->olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connector_olap_handler); + + // start connect operation + BOOL res = o->fnConnectEx(o->sock, &sysaddr.addr.generic, sysaddr.len, NULL, 0, NULL, &o->olap.olap); + if (res == FALSE && WSAGetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "ConnectEx failed (%d)", WSAGetLastError()); + goto fail2; + } + + // set busy + o->busy = 1; + + // set not ready + o->ready = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + BReactorIOCPOverlapped_Free(&o->olap); +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BConnector_Free (BConnector *o) +{ + DebugObject_Free(&o->d_obj); + + if (o->sock != INVALID_SOCKET) { + connector_abort(o); + } +} + +int BConnection_Init (BConnection *o, struct BConnection_source source, BReactor *reactor, void *user, + BConnection_handler handler) +{ + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + DebugObject_Access(&listener->d_obj); + ASSERT(BPending_IsSet(&listener->next_job)) + ASSERT(!listener->busy) + ASSERT(listener->ready) + } break; + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + DebugObject_Access(&connector->d_obj); + ASSERT(connector->reactor == reactor) + ASSERT(connector->sock != INVALID_SOCKET) + ASSERT(!connector->busy) + ASSERT(connector->ready) + } break; + default: ASSERT(0); + } + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + switch (source.type) { + case BCONNECTION_SOURCE_TYPE_LISTENER: { + BListener *listener = source.u.listener.listener; + + // grab new socket from listener + o->sock = listener->newsock; + listener->ready = 0; + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // return address + if (source.u.listener.out_addr) { + struct sockaddr *addr_local; + struct sockaddr *addr_remote; + int len_local; + int len_remote; + listener->fnGetAcceptExSockaddrs(listener->addrbuf, 0, sizeof(struct BListener_addrbuf_stub), sizeof(struct BListener_addrbuf_stub), + &addr_local, &len_local, &addr_remote, &len_remote); + + struct sys_addr sysaddr; + + ASSERT_FORCE(len_remote >= 0) + ASSERT_FORCE(len_remote <= sizeof(sysaddr.addr)) + + memcpy((uint8_t *)&sysaddr.addr, (uint8_t *)addr_remote, len_remote); + sysaddr.len = len_remote; + + addr_sys_to_socket(source.u.listener.out_addr, sysaddr); + } + } break; + + case BCONNECTION_SOURCE_TYPE_CONNECTOR: { + BConnector *connector = source.u.connector.connector; + + // grab fd from connector + o->sock = connector->sock; + connector->sock = INVALID_SOCKET; + + // release connector resources + connector_abort(connector); + } break; + } + + // set not aborted + o->aborted = 0; + + // init send olap + BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_send_olap_handler); + + // set send not inited + o->send.inited = 0; + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)connection_recv_olap_handler); + + // set recv not closed + o->recv.closed = 0; + + // set recv not inited + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + return 0; +} + +void BConnection_Free (BConnection *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + if (!o->aborted) { + connection_abort(o); + } +} + +void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler handler) +{ + DebugObject_Access(&o->d_obj); + + // set handlers + o->user = user; + o->handler = handler; +} + +int BConnection_SetSendBuffer (BConnection *o, int buf_size) +{ + DebugObject_Access(&o->d_obj); + + if (setsockopt(o->sock, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(buf_size)) < 0) { + BLog(BLOG_ERROR, "setsockopt failed"); + return 0; + } + + return 1; +} + +void BConnection_SendAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->send.inited) + + // init interface + StreamPassInterface_Init(&o->send.iface, (StreamPassInterface_handler_send)connection_send_iface_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // set not busy + o->send.busy = 0; + + // set inited + o->send.inited = 1; +} + +void BConnection_SendAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // abort if busy + if (o->send.busy && !o->aborted) { + connection_abort(o); + } + + // free interface + StreamPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +StreamPassInterface * BConnection_SendAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BConnection_RecvAsync_Init (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.closed) + ASSERT(!o->aborted) + ASSERT(!o->recv.inited) + + // init interface + StreamRecvInterface_Init(&o->recv.iface, (StreamRecvInterface_handler_recv)connection_recv_iface_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set not busy + o->recv.busy = 0; + + // set inited + o->recv.inited = 1; +} + +void BConnection_RecvAsync_Free (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // abort if busy + if (o->recv.busy && !o->aborted) { + connection_abort(o); + } + + // free interface + StreamRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +StreamRecvInterface * BConnection_RecvAsync_GetIf (BConnection *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/external/badvpn_dns/system/BConnection_win.h b/external/badvpn_dns/system/BConnection_win.h new file mode 100644 index 00000000..da9a8ed5 --- /dev/null +++ b/external/badvpn_dns/system/BConnection_win.h @@ -0,0 +1,101 @@ +/** + * @file BConnection_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BListener_addrbuf_stub { + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + uint8_t extra[16]; +}; + +struct BListener_s { + BReactor *reactor; + void *user; + BListener_handler handler; + int sys_family; + SOCKET sock; + LPFN_ACCEPTEX fnAcceptEx; + LPFN_GETACCEPTEXSOCKADDRS fnGetAcceptExSockaddrs; + BReactorIOCPOverlapped olap; + SOCKET newsock; + uint8_t addrbuf[2 * sizeof(struct BListener_addrbuf_stub)]; + BPending next_job; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnector_s { + BReactor *reactor; + void *user; + BConnector_handler handler; + SOCKET sock; + LPFN_CONNECTEX fnConnectEx; + BReactorIOCPOverlapped olap; + int busy; + int ready; + DebugObject d_obj; +}; + +struct BConnection_s { + BReactor *reactor; + void *user; + BConnection_handler handler; + SOCKET sock; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int inited; + StreamPassInterface iface; + int busy; + int busy_data_len; + } send; + struct { + BReactorIOCPOverlapped olap; + int closed; + int inited; + StreamRecvInterface iface; + int busy; + int busy_data_len; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/external/badvpn_dns/system/BDatagram.h b/external/badvpn_dns/system/BDatagram.h new file mode 100644 index 00000000..33efb451 --- /dev/null +++ b/external/badvpn_dns/system/BDatagram.h @@ -0,0 +1,209 @@ +/** + * @file BDatagram.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BDATAGRAM +#define BADVPN_SYSTEM_BDATAGRAM + +#include +#include +#include +#include +#include +#include + +struct BDatagram_s; + +/** + * Represents datagram communication. This is usually UDP, but may also be Linux packet sockets. + * Sending and receiving is performed via {@link PacketPassInterface} and {@link PacketRecvInterface}, + * respectively. + */ +typedef struct BDatagram_s BDatagram; + +#define BDATAGRAM_EVENT_ERROR 1 + +/** + * Handler called when an error occurs with the datagram object. + * The datagram object is no longer usable and must be freed from withing the job closure of + * this handler. No further I/O, interface initialization, binding and send address setting + * must occur. + * + * @param user as in {@link BDatagram_Init} + * @param event always BDATAGRAM_EVENT_ERROR + */ +typedef void (*BDatagram_handler) (void *user, int event); + +/** + * Checks if the given address family (from {@link BAddr.h}) is supported by {@link BDatagram} + * and related objects. + * + * @param family family to check + * @return 1 if supported, 0 if not + */ +int BDatagram_AddressFamilySupported (int family); + +/** + * Initializes the object. + * {@link BNetwork_GlobalInit} must have been done. + * + * @param o the object + * @param family address family. Must be supported according to {@link BDatagram_AddressFamilySupported}. + * @param reactor reactor we live in + * @param user argument to handler + * @param handler handler called when an error occurs + * @return 1 on success, 0 on failure + */ +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) WARN_UNUSED; + +/** + * Frees the object. + * The send and receive interfaces must not be initialized. + * + * @param o the object + */ +void BDatagram_Free (BDatagram *o); + +/** + * Binds to the given local address. + * May initiate I/O. + * + * @param o the object + * @param addr address to bind to. Its family must be supported according to {@link BDatagram_AddressFamilySupported}. + * @return 1 on success, 0 on failure + */ +int BDatagram_Bind (BDatagram *o, BAddr addr) WARN_UNUSED; + +/** + * Sets addresses for sending. + * May initiate I/O. + * + * @param o the object + * @param remote_addr destination address for sending datagrams. Its family must be supported according + * to {@link BDatagram_AddressFamilySupported}. + * @param local_addr local source IP address. May be an invalid address, otherwise its family must be + * supported according to {@link BDatagram_AddressFamilySupported}. + */ +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr); + +/** + * Returns the remote and local address of the last datagram received. + * Fails if and only if no datagrams have been received yet. + * + * @param o the object + * @param remote_addr returns the remote source address of the datagram. May be an invalid address, theoretically. + * @param local_addr returns the local destination IP address. May be an invalid address. + * @return 1 on success, 0 on failure + */ +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); + +#ifndef BADVPN_USE_WINAPI +/** + * Returns the underlying socket file descriptor of the datagram object. + * Available on Unix-like systems only. + * + * @param o the object + * @return file descriptor + */ +int BDatagram_GetFd (BDatagram *o); +#endif + +/** + * Sets the SO_REUSEADDR option for the underlying socket. + * + * @param o the object + * @param reuse value of the option. Must be 0 or 1. + */ +int BDatagram_SetReuseAddr (BDatagram *o, int reuse); + +/** + * Initializes the send interface. + * The send interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_SendAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the send interface. + * The send interface must be initialized. + * If the send interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_SendAsync_Free (BDatagram *o); + +/** + * Returns the send interface. + * The send interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_SendAsync_Init}. + * + * @param o the object + * @return send interface + */ +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o); + +/** + * Initializes the receive interface. + * The receive interface must not be initialized. + * + * @param o the object + * @param mtu maximum transmission unit. Must be >=0. + */ +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu); + +/** + * Frees the receive interface. + * The receive interface must be initialized. + * If the receive interface was busy when this is called, the datagram object is no longer usable and must be + * freed before any further I/O or interface initialization. + * + * @param o the object + */ +void BDatagram_RecvAsync_Free (BDatagram *o); + +/** + * Returns the receive interface. + * The receive interface must be initialized. + * The MTU of the interface will be as in {@link BDatagram_RecvAsync_Init}. + * + * @param o the object + * @return receive interface + */ +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o); + +#ifdef BADVPN_USE_WINAPI +#include "BDatagram_win.h" +#else +#include "BDatagram_unix.h" +#endif + +#endif diff --git a/external/badvpn_dns/system/BDatagram_unix.c b/external/badvpn_dns/system/BDatagram_unix.c new file mode 100644 index 00000000..67853db4 --- /dev/null +++ b/external/badvpn_dns/system/BDatagram_unix.c @@ -0,0 +1,855 @@ +/** + * @file BDatagram_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef BADVPN_LINUX +# include +# include +#endif + +#include +#include + +#include "BDatagram.h" + +#include + +struct sys_addr { + socklen_t len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +#ifdef BADVPN_LINUX + struct sockaddr_ll packet; +#endif + } addr; +}; + +static int family_socket_to_sys (int family); +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr); +static void set_pktinfo (int fd, int family); +static void report_error (BDatagram *o); +static void do_send (BDatagram *o); +static void do_recv (BDatagram *o); +static void fd_handler (BDatagram *o, int events); +static void send_job_handler (BDatagram *o); +static void recv_job_handler (BDatagram *o); +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len); +static void recv_if_handler_recv (BDatagram *o, uint8_t *data); + +static int family_socket_to_sys (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + return AF_INET; + case BADDR_TYPE_IPV6: + return AF_INET6; +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: + return AF_PACKET; +#endif + } + + ASSERT(0); + return 0; +} + +static void addr_socket_to_sys (struct sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: { + ASSERT(addr.packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET) + memset(&out->addr.packet, 0, sizeof(out->addr.packet)); + out->len = sizeof(out->addr.packet); + out->addr.packet.sll_family = AF_PACKET; + out->addr.packet.sll_protocol = addr.packet.phys_proto; + out->addr.packet.sll_ifindex = addr.packet.interface_index; + out->addr.packet.sll_hatype = 1; // linux/if_arp.h: #define ARPHRD_ETHER 1 + switch (addr.packet.packet_type) { + case BADDR_PACKET_PACKET_TYPE_HOST: + out->addr.packet.sll_pkttype = PACKET_HOST; + break; + case BADDR_PACKET_PACKET_TYPE_BROADCAST: + out->addr.packet.sll_pkttype = PACKET_BROADCAST; + break; + case BADDR_PACKET_PACKET_TYPE_MULTICAST: + out->addr.packet.sll_pkttype = PACKET_MULTICAST; + break; + case BADDR_PACKET_PACKET_TYPE_OTHERHOST: + out->addr.packet.sll_pkttype = PACKET_OTHERHOST; + break; + case BADDR_PACKET_PACKET_TYPE_OUTGOING: + out->addr.packet.sll_pkttype = PACKET_OUTGOING; + break; + default: + ASSERT(0); + } + out->addr.packet.sll_halen = 6; + memcpy(out->addr.packet.sll_addr, addr.packet.phys_addr, 6); + } break; +#endif + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + +#ifdef BADVPN_LINUX + case AF_PACKET: { + if (addr.len < offsetof(struct sockaddr_ll, sll_addr) + 6) { + goto fail; + } + if (addr.addr.packet.sll_hatype != 1) { // linux/if_arp.h: #define ARPHRD_ETHER 1 + goto fail; + } + int packet_type; + switch (addr.addr.packet.sll_pkttype) { + case PACKET_HOST: + packet_type = BADDR_PACKET_PACKET_TYPE_HOST; + break; + case PACKET_BROADCAST: + packet_type = BADDR_PACKET_PACKET_TYPE_BROADCAST; + break; + case PACKET_MULTICAST: + packet_type = BADDR_PACKET_PACKET_TYPE_MULTICAST; + break; + case PACKET_OTHERHOST: + packet_type = BADDR_PACKET_PACKET_TYPE_OTHERHOST; + break; + case PACKET_OUTGOING: + packet_type = BADDR_PACKET_PACKET_TYPE_OUTGOING; + break; + default: + goto fail; + } + if (addr.addr.packet.sll_halen != 6) { + goto fail; + } + BAddr_InitPacket(out, addr.addr.packet.sll_protocol, addr.addr.packet.sll_ifindex, BADDR_PACKET_HEADER_TYPE_ETHERNET, packet_type, addr.addr.packet.sll_addr); + } break; +#endif + + fail: + default: { + BAddr_InitNone(out); + } break; + } +} + +static void set_pktinfo (int fd, int family) +{ + int opt = 1; + + switch (family) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_RECVDSTADDR) failed"); + } +#else + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed"); + } +#endif + } break; + +#ifdef IPV6_RECVPKTINFO + case BADDR_TYPE_IPV6: { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IPV6_RECVPKTINFO) failed"); + } + } break; +#endif + } +} + +static void report_error (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR)); + return; +} + +static void do_send (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + // limit + if (!BReactorLimit_Increment(&o->send.limit)) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + // convert destination address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, o->send.remote_addr); + + struct iovec iov; + iov.iov_base = (uint8_t *)o->send.busy_data; + iov.iov_len = o->send.busy_data_len; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sysaddr.len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + size_t controllen = 0; + + switch (o->send.local_addr.type) { + case BADDR_TYPE_IPV4: { +#ifdef BADVPN_FREEBSD + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + addrinfo->s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_addr)); +#else + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + pktinfo->ipi_spec_dst.s_addr = o->send.local_addr.ipv4; + controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); +#endif + } break; + + case BADDR_TYPE_IPV6: { + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16); + controllen += CMSG_SPACE(sizeof(struct in6_pktinfo)); + } break; + } + + msg.msg_controllen = controllen; + + if (msg.msg_controllen == 0) { + msg.msg_control = NULL; + } + + // send + int bytes = sendmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "send failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->send.busy_data_len) + + if (bytes < o->send.busy_data_len) { + BLog(BLOG_ERROR, "send sent too little"); + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + // set not busy + o->send.busy = 0; + + // done + PacketPassInterface_Done(&o->send.iface); +} + +static void do_recv (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + // limit + if (!BReactorLimit_Increment(&o->recv.limit)) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + struct sys_addr sysaddr; + + struct iovec iov; + iov.iov_base = o->recv.busy_data; + iov.iov_len = o->recv.mtu; + + union { +#ifdef BADVPN_FREEBSD + char in[CMSG_SPACE(sizeof(struct in_addr))]; +#else + char in[CMSG_SPACE(sizeof(struct in_pktinfo))]; +#endif + char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sysaddr.addr.generic; + msg.msg_namelen = sizeof(sysaddr.addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cdata; + msg.msg_controllen = sizeof(cdata); + + // recv + int bytes = recvmsg(o->fd, &msg, 0); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // wait for fd + o->wait_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + return; + } + + BLog(BLOG_ERROR, "recv failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->recv.mtu) + + // read returned address + sysaddr.len = msg.msg_namelen; + addr_sys_to_socket(&o->recv.remote_addr, sysaddr); + + // read returned local address + BIPAddr_InitInvalid(&o->recv.local_addr); + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { +#ifdef BADVPN_FREEBSD + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { + struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, addrinfo->s_addr); + } +#else + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr); + } +#endif + else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr); + } + } + + // set have addresses + o->recv.have_addrs = 1; + + // set not busy + o->recv.busy = 0; + + // done + PacketRecvInterface_Done(&o->recv.iface, bytes); +} + +static void fd_handler (BDatagram *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + // clear handled events + o->wait_events &= ~events; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + int have_send = 0; + int have_recv = 0; + + if ((events & BREACTOR_WRITE) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->send.inited && o->send.busy && o->send.have_addrs)) { + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + have_send = 1; + } + + if ((events & BREACTOR_READ) || ((events & (BREACTOR_ERROR|BREACTOR_HUP)) && o->recv.inited && o->recv.busy && o->recv.started)) { + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + have_recv = 1; + } + + if (have_send) { + if (have_recv) { + BPending_Set(&o->recv.job); + } + + do_send(o); + return; + } + + if (have_recv) { + do_recv(o); + return; + } + + BLog(BLOG_ERROR, "fd error event"); + report_error(o); + return; +} + +static void send_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(o->send.busy) + ASSERT(o->send.have_addrs) + + do_send(o); + return; +} + +static void recv_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(o->recv.busy) + ASSERT(o->recv.started) + + do_recv(o); + return; +} + +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->send.inited) + ASSERT(!o->send.busy) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->send.mtu) + + // remember data + o->send.busy_data = data; + o->send.busy_data_len = data_len; + + // set busy + o->send.busy = 1; + + // if have no addresses, wait + if (!o->send.have_addrs) { + return; + } + + // set job + BPending_Set(&o->send.job); +} + +static void recv_if_handler_recv (BDatagram *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(o->recv.inited) + ASSERT(!o->recv.busy) + + // remember data + o->recv.busy_data = data; + + // set busy + o->recv.busy = 1; + + // if recv not started yet, wait + if (!o->recv.started) { + return; + } + + // set job + BPending_Set(&o->recv.job); +} + +int BDatagram_AddressFamilySupported (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + case BADDR_TYPE_IPV6: +#ifdef BADVPN_LINUX + case BADDR_TYPE_PACKET: +#endif + return 1; + } + + return 0; +} + +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) +{ + ASSERT(BDatagram_AddressFamilySupported(family)) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // init fd + if ((o->fd = socket(family_socket_to_sys(family), SOCK_DGRAM, 0)) < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail0; + } + + // set fd non-blocking + if (!badvpn_set_nonblocking(o->fd)) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + // enable receiving pktinfo + set_pktinfo(o->fd, family); + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + // set no wait events + o->wait_events = 0; + + // init limits + BReactorLimit_Init(&o->send.limit, o->reactor, BDATAGRAM_SEND_LIMIT); + BReactorLimit_Init(&o->recv.limit, o->reactor, BDATAGRAM_RECV_LIMIT); + + // set have no send and recv addresses + o->send.have_addrs = 0; + o->recv.have_addrs = 0; + + // set recv not started + o->recv.started = 0; + + // set send and recv not inited + o->send.inited = 0; + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void BDatagram_Free (BDatagram *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + // free limits + BReactorLimit_Free(&o->recv.limit); + BReactorLimit_Free(&o->send.limit); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free fd + if (close(o->fd) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +int BDatagram_Bind (BDatagram *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // translate address + struct sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // bind + if (bind(o->fd, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + return 0; + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.busy) { + BPending_Set(&o->recv.job); + } + } + + return 1; +} + +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(BDatagram_AddressFamilySupported(remote_addr.type)) + ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type)) + + // set addresses + o->send.remote_addr = remote_addr; + o->send.local_addr = local_addr; + + if (!o->send.have_addrs) { + // set have addresses + o->send.have_addrs = 1; + + // start sending + if (o->send.inited && o->send.busy) { + BPending_Set(&o->send.job); + } + } +} + +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + if (!o->recv.have_addrs) { + return 0; + } + + *remote_addr = o->recv.remote_addr; + *local_addr = o->recv.local_addr; + return 1; +} + +int BDatagram_GetFd (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + + return o->fd; +} + +int BDatagram_SetReuseAddr (BDatagram *o, int reuse) +{ + DebugObject_Access(&o->d_obj); + ASSERT(reuse == 0 || reuse == 1) + + if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { + return 0; + } + + return 1; +} + +void BDatagram_SendAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->send.inited) + ASSERT(mtu >= 0) + + // init arguments + o->send.mtu = mtu; + + // init interface + PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o); + + // set not busy + o->send.busy = 0; + + // set inited + o->send.inited = 1; +} + +void BDatagram_SendAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // update events + o->wait_events &= ~BREACTOR_WRITE; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->send.job); + + // free interface + PacketPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(mtu >= 0) + + // init arguments + o->recv.mtu = mtu; + + // init interface + PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o); + + // set not busy + o->recv.busy = 0; + + // set inited + o->recv.inited = 1; +} + +void BDatagram_RecvAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // update events + o->wait_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->wait_events); + + // free job + BPending_Free(&o->recv.job); + + // free interface + PacketRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/external/badvpn_dns/system/BDatagram_unix.h b/external/badvpn_dns/system/BDatagram_unix.h new file mode 100644 index 00000000..3ef02628 --- /dev/null +++ b/external/badvpn_dns/system/BDatagram_unix.h @@ -0,0 +1,71 @@ +/** + * @file BDatagram_unix.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#define BDATAGRAM_SEND_LIMIT 2 +#define BDATAGRAM_RECV_LIMIT 2 + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + int fd; + BFileDescriptor bfd; + int wait_events; + struct { + BReactorLimit limit; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int busy; + const uint8_t *busy_data; + int busy_data_len; + } send; + struct { + BReactorLimit limit; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int busy; + uint8_t *busy_data; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/external/badvpn_dns/system/BDatagram_win.c b/external/badvpn_dns/system/BDatagram_win.c new file mode 100644 index 00000000..0528866d --- /dev/null +++ b/external/badvpn_dns/system/BDatagram_win.c @@ -0,0 +1,755 @@ +/** + * @file BDatagram_win.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "BDatagram.h" + +#include + +static int family_socket_to_sys (int family); +static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr); +static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr); +static void set_pktinfo (SOCKET sock, int family); +static void report_error (BDatagram *o); +static void datagram_abort (BDatagram *o); +static void start_send (BDatagram *o); +static void start_recv (BDatagram *o); +static void send_job_handler (BDatagram *o); +static void recv_job_handler (BDatagram *o); +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len); +static void recv_if_handler_recv (BDatagram *o, uint8_t *data); +static void send_olap_handler (BDatagram *o, int event, DWORD bytes); +static void recv_olap_handler (BDatagram *o, int event, DWORD bytes); + +static int family_socket_to_sys (int family) +{ + switch (family) { + case BADDR_TYPE_IPV4: + return AF_INET; + case BADDR_TYPE_IPV6: + return AF_INET6; + } + + ASSERT(0); + return 0; +} + +static void addr_socket_to_sys (struct BDatagram_sys_addr *out, BAddr addr) +{ + switch (addr.type) { + case BADDR_TYPE_IPV4: { + out->len = sizeof(out->addr.ipv4); + memset(&out->addr.ipv4, 0, sizeof(out->addr.ipv4)); + out->addr.ipv4.sin_family = AF_INET; + out->addr.ipv4.sin_port = addr.ipv4.port; + out->addr.ipv4.sin_addr.s_addr = addr.ipv4.ip; + } break; + + case BADDR_TYPE_IPV6: { + out->len = sizeof(out->addr.ipv6); + memset(&out->addr.ipv6, 0, sizeof(out->addr.ipv6)); + out->addr.ipv6.sin6_family = AF_INET6; + out->addr.ipv6.sin6_port = addr.ipv6.port; + out->addr.ipv6.sin6_flowinfo = 0; + memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr.ipv6.ip, 16); + out->addr.ipv6.sin6_scope_id = 0; + } break; + + default: ASSERT(0); + } +} + +static void addr_sys_to_socket (BAddr *out, struct BDatagram_sys_addr addr) +{ + switch (addr.addr.generic.sa_family) { + case AF_INET: { + ASSERT(addr.len == sizeof(struct sockaddr_in)) + BAddr_InitIPv4(out, addr.addr.ipv4.sin_addr.s_addr, addr.addr.ipv4.sin_port); + } break; + + case AF_INET6: { + ASSERT(addr.len == sizeof(struct sockaddr_in6)) + BAddr_InitIPv6(out, addr.addr.ipv6.sin6_addr.s6_addr, addr.addr.ipv6.sin6_port); + } break; + + default: { + BAddr_InitNone(out); + } break; + } +} + +static void set_pktinfo (SOCKET sock, int family) +{ + DWORD opt = 1; + + switch (family) { + case BADDR_TYPE_IPV4: { + if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IP_PKTINFO) failed"); + } + } break; + + case BADDR_TYPE_IPV6: { + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&opt, sizeof(opt)) < 0) { + BLog(BLOG_ERROR, "setsockopt(IPV6_PKTINFO) failed"); + } + } break; + } +} + +static void report_error (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + + // report error + DEBUGERROR(&o->d_err, o->handler(o->user, BDATAGRAM_EVENT_ERROR)); + return; +} + +static void datagram_abort (BDatagram *o) +{ + ASSERT(!o->aborted) + + // cancel I/O + if ((o->recv.inited && o->recv.data_have && o->recv.data_busy) || (o->send.inited && o->send.data_len >= 0 && o->send.data_busy)) { + if (!CancelIo((HANDLE)o->sock)) { + BLog(BLOG_ERROR, "CancelIo failed"); + } + } + + // close socket + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } + + // wait for receiving to complete + if (o->recv.inited && o->recv.data_have && o->recv.data_busy) { + BReactorIOCPOverlapped_Wait(&o->recv.olap, NULL, NULL); + } + + // wait for sending to complete + if (o->send.inited && o->send.data_len >= 0 && o->send.data_busy) { + BReactorIOCPOverlapped_Wait(&o->send.olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv.olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send.olap); + + // set aborted + o->aborted = 1; +} + +static void start_send (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(!o->send.data_busy) + ASSERT(o->send.have_addrs) + + // convert destination address + addr_socket_to_sys(&o->send.sysaddr, o->send.remote_addr); + + WSABUF buf; + buf.buf = (char *)o->send.data; + buf.len = (o->send.data_len > ULONG_MAX ? ULONG_MAX : o->send.data_len); + + memset(&o->send.olap.olap, 0, sizeof(o->send.olap.olap)); + + if (o->fnWSASendMsg) { + o->send.msg.name = &o->send.sysaddr.addr.generic; + o->send.msg.namelen = o->send.sysaddr.len; + o->send.msg.lpBuffers = &buf; + o->send.msg.dwBufferCount = 1; + o->send.msg.Control.buf = (char *)&o->send.cdata; + o->send.msg.Control.len = sizeof(o->send.cdata); + o->send.msg.dwFlags = 0; + + int sum = 0; + + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->send.msg); + + switch (o->send.local_addr.type) { + case BADDR_TYPE_IPV4: { + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg); + pktinfo->ipi_addr.s_addr = o->send.local_addr.ipv4; + sum += WSA_CMSG_SPACE(sizeof(struct in_pktinfo)); + } break; + case BADDR_TYPE_IPV6: { + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg); + memcpy(pktinfo->ipi6_addr.s6_addr, o->send.local_addr.ipv6, 16); + sum += WSA_CMSG_SPACE(sizeof(struct in6_pktinfo)); + } break; + } + + o->send.msg.Control.len = sum; + + if (o->send.msg.Control.len == 0) { + o->send.msg.Control.buf = NULL; + } + + // send + int res = o->fnWSASendMsg(o->sock, &o->send.msg, 0, NULL, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASendMsg failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } else { + // send + int res = WSASendTo(o->sock, &buf, 1, NULL, 0, &o->send.sysaddr.addr.generic, o->send.sysaddr.len, &o->send.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSASendTo failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } + + // set busy + o->send.data_busy = 1; +} + +static void start_recv (BDatagram *o) +{ + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(!o->recv.data_busy) + ASSERT(o->recv.started) + + WSABUF buf; + buf.buf = (char *)o->recv.data; + buf.len = (o->recv.mtu > ULONG_MAX ? ULONG_MAX : o->recv.mtu); + + memset(&o->recv.olap.olap, 0, sizeof(o->recv.olap.olap)); + + if (o->fnWSARecvMsg) { + o->recv.msg.name = &o->recv.sysaddr.addr.generic; + o->recv.msg.namelen = sizeof(o->recv.sysaddr.addr); + o->recv.msg.lpBuffers = &buf; + o->recv.msg.dwBufferCount = 1; + o->recv.msg.Control.buf = (char *)&o->recv.cdata; + o->recv.msg.Control.len = sizeof(o->recv.cdata); + o->recv.msg.dwFlags = 0; + + // recv + int res = o->fnWSARecvMsg(o->sock, &o->recv.msg, NULL, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecvMsg failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } else { + o->recv.sysaddr.len = sizeof(o->recv.sysaddr.addr); + + // recv + DWORD flags = 0; + int res = WSARecvFrom(o->sock, &buf, 1, NULL, &flags, &o->recv.sysaddr.addr.generic, &o->recv.sysaddr.len, &o->recv.olap.olap, NULL); + if (res == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { + BLog(BLOG_ERROR, "WSARecvFrom failed (%d)", WSAGetLastError()); + report_error(o); + return; + } + } + + // set busy + o->recv.data_busy = 1; +} + +static void send_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(!o->send.data_busy) + ASSERT(o->send.have_addrs) + + // send + start_send(o); + return; +} + +static void recv_job_handler (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(!o->recv.data_busy) + ASSERT(o->recv.started) + + // recv + start_recv(o); + return; +} + +static void send_if_handler_send (BDatagram *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len == -1) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->send.mtu) + + // remember data + o->send.data = data; + o->send.data_len = data_len; + o->send.data_busy = 0; + + // if have no addresses, wait + if (!o->send.have_addrs) { + return; + } + + // send + start_send(o); + return; +} + +static void recv_if_handler_recv (BDatagram *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(!o->recv.data_have) + + // remember data + o->recv.data = data; + o->recv.data_have = 1; + o->recv.data_busy = 0; + + // if recv not started yet, wait + if (!o->recv.started) { + return; + } + + // recv + start_recv(o); + return; +} + +static void send_olap_handler (BDatagram *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->send.inited) + ASSERT(o->send.data_len >= 0) + ASSERT(o->send.data_busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->send.data_busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "sending failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->send.data_len) + + if (bytes < o->send.data_len) { + BLog(BLOG_ERROR, "sent too little"); + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.data_have) { + ASSERT(!o->recv.data_busy) + + BPending_Set(&o->recv.job); + } + } + + // set no data + o->send.data_len = -1; + + // done + PacketPassInterface_Done(&o->send.iface); +} + +static void recv_olap_handler (BDatagram *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(o->recv.inited) + ASSERT(o->recv.data_have) + ASSERT(o->recv.data_busy) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set not busy + o->recv.data_busy = 0; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "receiving failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->recv.mtu) + + if (o->fnWSARecvMsg) { + o->recv.sysaddr.len = o->recv.msg.namelen; + } + + // read remote address + addr_sys_to_socket(&o->recv.remote_addr, o->recv.sysaddr); + + // read local address + BIPAddr_InitInvalid(&o->recv.local_addr); + if (o->fnWSARecvMsg) { + for (WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&o->recv.msg); cmsg; cmsg = WSA_CMSG_NXTHDR(&o->recv.msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)WSA_CMSG_DATA(cmsg); + BIPAddr_InitIPv4(&o->recv.local_addr, pktinfo->ipi_addr.s_addr); + } + else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)WSA_CMSG_DATA(cmsg); + BIPAddr_InitIPv6(&o->recv.local_addr, pktinfo->ipi6_addr.s6_addr); + } + } + } + + // set have addresses + o->recv.have_addrs = 1; + + // set no data + o->recv.data_have = 0; + + // done + PacketRecvInterface_Done(&o->recv.iface, bytes); +} + +int BDatagram_AddressFamilySupported (int family) +{ + return (family == BADDR_TYPE_IPV4 || family == BADDR_TYPE_IPV6); +} + +int BDatagram_Init (BDatagram *o, int family, BReactor *reactor, void *user, + BDatagram_handler handler) +{ + ASSERT(BDatagram_AddressFamilySupported(family)) + ASSERT(handler) + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // init socket + if ((o->sock = WSASocket(family_socket_to_sys(family), SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { + BLog(BLOG_ERROR, "WSASocket failed"); + goto fail0; + } + + DWORD out_bytes; + + // obtain WSASendMsg + GUID guid1 = WSAID_WSASENDMSG; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid1, sizeof(guid1), &o->fnWSASendMsg, sizeof(o->fnWSASendMsg), &out_bytes, NULL, NULL) != 0) { + o->fnWSASendMsg = NULL; + } + + // obtain WSARecvMsg + GUID guid2 = WSAID_WSARECVMSG; + if (WSAIoctl(o->sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid2, sizeof(guid2), &o->fnWSARecvMsg, sizeof(o->fnWSARecvMsg), &out_bytes, NULL, NULL) != 0) { + BLog(BLOG_ERROR, "failed to obtain WSARecvMsg"); + o->fnWSARecvMsg = NULL; + } + + // associate with IOCP + if (!CreateIoCompletionPort((HANDLE)o->sock, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail1; + } + + // enable receiving pktinfo + set_pktinfo(o->sock, family); + + // set not aborted + o->aborted = 0; + + // init send olap + BReactorIOCPOverlapped_Init(&o->send.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)send_olap_handler); + + // set have no send addrs + o->send.have_addrs = 0; + + // set send not inited + o->send.inited = 0; + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv.olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler); + + // set recv not started + o->recv.started = 0; + + // set have no recv addrs + o->recv.have_addrs = 0; + + // set recv not inited + o->recv.inited = 0; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (closesocket(o->sock) == SOCKET_ERROR) { + BLog(BLOG_ERROR, "closesocket failed"); + } +fail0: + return 0; +} + +void BDatagram_Free (BDatagram *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + ASSERT(!o->recv.inited) + ASSERT(!o->send.inited) + + if (!o->aborted) { + datagram_abort(o); + } +} + +int BDatagram_Bind (BDatagram *o, BAddr addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(BDatagram_AddressFamilySupported(addr.type)) + + // translate address + struct BDatagram_sys_addr sysaddr; + addr_socket_to_sys(&sysaddr, addr); + + // bind + if (bind(o->sock, &sysaddr.addr.generic, sysaddr.len) < 0) { + BLog(BLOG_ERROR, "bind failed"); + return 0; + } + + // if recv wasn't started yet, start it + if (!o->recv.started) { + // set recv started + o->recv.started = 1; + + // continue receiving + if (o->recv.inited && o->recv.data_have) { + ASSERT(!o->recv.data_busy) + + BPending_Set(&o->recv.job); + } + } + + return 1; +} + +void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(BDatagram_AddressFamilySupported(remote_addr.type)) + ASSERT(local_addr.type == BADDR_TYPE_NONE || BDatagram_AddressFamilySupported(local_addr.type)) + + // set addresses + o->send.remote_addr = remote_addr; + o->send.local_addr = local_addr; + + // set have addresses + o->send.have_addrs = 1; + + // start sending + if (o->send.inited && o->send.data_len >= 0 && !o->send.data_busy) { + BPending_Set(&o->send.job); + } +} + +int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + if (!o->recv.have_addrs) { + return 0; + } + + *remote_addr = o->recv.remote_addr; + *local_addr = o->recv.local_addr; + return 1; +} + +int BDatagram_SetReuseAddr (BDatagram *o, int reuse) +{ + DebugObject_Access(&o->d_obj); + ASSERT(reuse == 0 || reuse == 1) + + if (setsockopt(o->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { + return 0; + } + + return 1; +} + +void BDatagram_SendAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->send.inited) + ASSERT(mtu >= 0) + + // init arguments + o->send.mtu = mtu; + + // init interface + PacketPassInterface_Init(&o->send.iface, o->send.mtu, (PacketPassInterface_handler_send)send_if_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->send.job, BReactor_PendingGroup(o->reactor), (BPending_handler)send_job_handler, o); + + // set have no data + o->send.data_len = -1; + + // set inited + o->send.inited = 1; +} + +void BDatagram_SendAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + // abort if busy + if (o->send.data_len >= 0 && o->send.data_busy && !o->aborted) { + datagram_abort(o); + } + + // free job + BPending_Free(&o->send.job); + + // free interface + PacketPassInterface_Free(&o->send.iface); + + // set not inited + o->send.inited = 0; +} + +PacketPassInterface * BDatagram_SendAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->send.inited) + + return &o->send.iface; +} + +void BDatagram_RecvAsync_Init (BDatagram *o, int mtu) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(!o->aborted) + ASSERT(!o->recv.inited) + ASSERT(mtu >= 0) + + // init arguments + o->recv.mtu = mtu; + + // init interface + PacketRecvInterface_Init(&o->recv.iface, o->recv.mtu, (PacketRecvInterface_handler_recv)recv_if_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // init job + BPending_Init(&o->recv.job, BReactor_PendingGroup(o->reactor), (BPending_handler)recv_job_handler, o); + + // set have no data + o->recv.data_have = 0; + + // set inited + o->recv.inited = 1; +} + +void BDatagram_RecvAsync_Free (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + // abort if busy + if (o->recv.data_have && o->recv.data_busy && !o->aborted) { + datagram_abort(o); + } + + // free job + BPending_Free(&o->recv.job); + + // free interface + PacketRecvInterface_Free(&o->recv.iface); + + // set not inited + o->recv.inited = 0; +} + +PacketRecvInterface * BDatagram_RecvAsync_GetIf (BDatagram *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->recv.inited) + + return &o->recv.iface; +} diff --git a/external/badvpn_dns/system/BDatagram_win.h b/external/badvpn_dns/system/BDatagram_win.h new file mode 100644 index 00000000..9831946d --- /dev/null +++ b/external/badvpn_dns/system/BDatagram_win.h @@ -0,0 +1,99 @@ +/** + * @file BDatagram_win.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#ifdef BADVPN_USE_SHIPPED_MSWSOCK +# include +#else +# include +#endif + +#include +#include + +struct BDatagram_sys_addr { + int len; + union { + struct sockaddr generic; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; +}; + +struct BDatagram_s { + BReactor *reactor; + void *user; + BDatagram_handler handler; + SOCKET sock; + LPFN_WSASENDMSG fnWSASendMsg; + LPFN_WSARECVMSG fnWSARecvMsg; + int aborted; + struct { + BReactorIOCPOverlapped olap; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketPassInterface iface; + BPending job; + int data_len; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } send; + struct { + BReactorIOCPOverlapped olap; + int started; + int have_addrs; + BAddr remote_addr; + BIPAddr local_addr; + int inited; + int mtu; + PacketRecvInterface iface; + BPending job; + int data_have; + uint8_t *data; + int data_busy; + struct BDatagram_sys_addr sysaddr; + union { + char in[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; + char in6[WSA_CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cdata; + WSAMSG msg; + } recv; + DebugError d_err; + DebugObject d_obj; +}; diff --git a/external/badvpn_dns/system/BInputProcess.c b/external/badvpn_dns/system/BInputProcess.c new file mode 100644 index 00000000..b3d096ae --- /dev/null +++ b/external/badvpn_dns/system/BInputProcess.c @@ -0,0 +1,211 @@ +/** + * @file BInputProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "BInputProcess.h" + +#include + +static void connection_handler (BInputProcess *o, int event); +static void process_handler (BInputProcess *o, int normally, uint8_t normally_exit_status); + +void connection_handler (BInputProcess *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pipe_fd >= 0) + + if (event == BCONNECTION_EVENT_RECVCLOSED) { + BLog(BLOG_INFO, "pipe closed"); + } else { + BLog(BLOG_ERROR, "pipe error"); + } + + // free pipe connection read interface + BConnection_RecvAsync_Free(&o->pipe_con); + + // free pipe connection + BConnection_Free(&o->pipe_con); + + // close pipe read end + ASSERT_FORCE(close(o->pipe_fd) == 0) + + // forget pipe + o->pipe_fd = -1; + + // call closed handler + o->handler_closed(o->user, (event != BCONNECTION_EVENT_RECVCLOSED)); + return; +} + +void process_handler (BInputProcess *o, int normally, uint8_t normally_exit_status) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + // free process + BProcess_Free(&o->process); + + // set not have process + o->have_process = 0; + + // call terminated handler + o->handler_terminated(o->user, normally, normally_exit_status); + return; +} + +int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *manager, void *user, + BInputProcess_handler_terminated handler_terminated, + BInputProcess_handler_closed handler_closed) +{ + BNetwork_Assert(); + + // init arguments + o->reactor = reactor; + o->manager = manager; + o->user = user; + o->handler_terminated = handler_terminated; + o->handler_closed = handler_closed; + + // create pipe + int pipefds[2]; + if (pipe(pipefds) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail0; + } + + // init pipe connection + if (!BConnection_Init(&o->pipe_con, BConnection_source_pipe(pipefds[0]), o->reactor, o, (BConnection_handler)connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // init pipe connection read interface + BConnection_RecvAsync_Init(&o->pipe_con); + + // remember pipe fds + o->pipe_fd = pipefds[0]; + o->pipe_write_fd = pipefds[1]; + + // set not started + o->started = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + ASSERT_FORCE(close(pipefds[0]) == 0) + ASSERT_FORCE(close(pipefds[1]) == 0) +fail0: + return 0; +} + +void BInputProcess_Free (BInputProcess *o) +{ + DebugObject_Free(&o->d_obj); + + if (!o->started) { + // close pipe write end + ASSERT_FORCE(close(o->pipe_write_fd) == 0) + } else { + // free process + if (o->have_process) { + BProcess_Free(&o->process); + } + } + + if (o->pipe_fd >= 0) { + // free pipe connection read interface + BConnection_RecvAsync_Free(&o->pipe_con); + + // free pipe connection + BConnection_Free(&o->pipe_con); + + // close pipe read end + ASSERT_FORCE(close(o->pipe_fd) == 0) + } +} + +int BInputProcess_Start (BInputProcess *o, const char *file, char *const argv[], const char *username) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->started) + + // start process + int fds[] = { o->pipe_write_fd, -1 }; + int fds_map[] = { 1 }; + if (!BProcess_InitWithFds(&o->process, o->manager, (BProcess_handler)process_handler, o, file, argv, username, fds, fds_map)) { + BLog(BLOG_ERROR, "BProcess_Init failed"); + goto fail0; + } + + // close pipe write end + ASSERT_FORCE(close(o->pipe_write_fd) == 0) + + // set started + o->started = 1; + + // set have process + o->have_process = 1; + + return 1; + +fail0: + return 0; +} + +int BInputProcess_Terminate (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + return BProcess_Terminate(&o->process); +} + +int BInputProcess_Kill (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->started) + ASSERT(o->have_process) + + return BProcess_Kill(&o->process); +} + +StreamRecvInterface * BInputProcess_GetInput (BInputProcess *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->pipe_fd >= 0) + + return BConnection_RecvAsync_GetIf(&o->pipe_con); +} diff --git a/external/badvpn_dns/system/BInputProcess.h b/external/badvpn_dns/system/BInputProcess.h new file mode 100644 index 00000000..7317a5d4 --- /dev/null +++ b/external/badvpn_dns/system/BInputProcess.h @@ -0,0 +1,65 @@ +/** + * @file BInputProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BINPUTPROCESS_H +#define BADVPN_BINPUTPROCESS_H + +#include +#include +#include +#include + +typedef void (*BInputProcess_handler_terminated) (void *user, int normally, uint8_t normally_exit_status); +typedef void (*BInputProcess_handler_closed) (void *user, int is_error); + +typedef struct { + BReactor *reactor; + BProcessManager *manager; + void *user; + BInputProcess_handler_terminated handler_terminated; + BInputProcess_handler_closed handler_closed; + int pipe_write_fd; + int started; + int have_process; + BProcess process; + int pipe_fd; + BConnection pipe_con; + DebugObject d_obj; +} BInputProcess; + +int BInputProcess_Init (BInputProcess *o, BReactor *reactor, BProcessManager *manager, void *user, + BInputProcess_handler_terminated handler_terminated, + BInputProcess_handler_closed handler_closed) WARN_UNUSED; +void BInputProcess_Free (BInputProcess *o); +int BInputProcess_Start (BInputProcess *o, const char *file, char *const argv[], const char *username); +int BInputProcess_Terminate (BInputProcess *o); +int BInputProcess_Kill (BInputProcess *o); +StreamRecvInterface * BInputProcess_GetInput (BInputProcess *o); + +#endif diff --git a/external/badvpn_dns/system/BLockReactor.c b/external/badvpn_dns/system/BLockReactor.c new file mode 100644 index 00000000..e9a27245 --- /dev/null +++ b/external/badvpn_dns/system/BLockReactor.c @@ -0,0 +1,131 @@ +/** + * @file BLockReactor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "BLockReactor.h" + +#include + +static void thread_signal_handler (BThreadSignal *thread_signal) +{ + BLockReactor *o = UPPER_OBJECT(thread_signal, BLockReactor, thread_signal); + DebugObject_Access(&o->d_obj); + + ASSERT_FORCE(sem_post(&o->sem1) == 0) + ASSERT_FORCE(sem_wait(&o->sem2) == 0) +} + +int BLockReactor_Init (BLockReactor *o, BReactor *reactor) +{ + o->reactor = reactor; + + if (!BThreadSignal_Init(&o->thread_signal, reactor, thread_signal_handler)) { + BLog(BLOG_ERROR, "BThreadSignal_Init failed"); + goto fail0; + } + + if (sem_init(&o->sem1, 0, 0) < 0) { + BLog(BLOG_ERROR, "sem_init failed"); + goto fail1; + } + + if (sem_init(&o->sem2, 0, 0) < 0) { + BLog(BLOG_ERROR, "sem_init failed"); + goto fail2; + } + +#ifndef NDEBUG + o->locked = 0; +#endif + + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + if (sem_close(&o->sem1) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } +fail1: + BThreadSignal_Free(&o->thread_signal); +fail0: + return 0; +} + +void BLockReactor_Free (BLockReactor *o) +{ + DebugObject_Free(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->locked) +#endif + + if (sem_destroy(&o->sem2) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } + if (sem_destroy(&o->sem1) < 0) { + BLog(BLOG_ERROR, "sem_close failed"); + } + BThreadSignal_Free(&o->thread_signal); +} + +int BLockReactor_Thread_Lock (BLockReactor *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(!o->locked) +#endif + + if (!BThreadSignal_Thread_Signal(&o->thread_signal)) { + return 0; + } + + ASSERT_FORCE(sem_wait(&o->sem1) == 0) + +#ifndef NDEBUG + o->locked = 1; +#endif + + return 1; +} + +void BLockReactor_Thread_Unlock (BLockReactor *o) +{ + DebugObject_Access(&o->d_obj); +#ifndef NDEBUG + ASSERT(o->locked) +#endif + +#ifndef NDEBUG + o->locked = 0; +#endif + + ASSERT_FORCE(sem_post(&o->sem2) == 0) +} diff --git a/external/badvpn_dns/system/BLockReactor.h b/external/badvpn_dns/system/BLockReactor.h new file mode 100644 index 00000000..84acab85 --- /dev/null +++ b/external/badvpn_dns/system/BLockReactor.h @@ -0,0 +1,58 @@ +/** + * @file BLockReactor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_LOCK_REACTOR_H +#define BADVPN_B_LOCK_REACTOR_H + +#include + +#include +#include +#include +#include + +typedef struct BLockReactor_s BLockReactor; + +struct BLockReactor_s { + BReactor *reactor; + BThreadSignal thread_signal; + sem_t sem1; + sem_t sem2; +#ifndef NDEBUG + int locked; +#endif + DebugObject d_obj; +}; + +int BLockReactor_Init (BLockReactor *o, BReactor *reactor) WARN_UNUSED; +void BLockReactor_Free (BLockReactor *o); +int BLockReactor_Thread_Lock (BLockReactor *o); +void BLockReactor_Thread_Unlock (BLockReactor *o); + +#endif diff --git a/external/badvpn_dns/system/BNetwork.c b/external/badvpn_dns/system/BNetwork.c new file mode 100644 index 00000000..b48ae610 --- /dev/null +++ b/external/badvpn_dns/system/BNetwork.c @@ -0,0 +1,99 @@ +/** + * @file BNetwork.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +extern int bnetwork_initialized; + +#ifndef BADVPN_PLUGIN +int bnetwork_initialized = 0; +#endif + +int BNetwork_GlobalInit (void) +{ + ASSERT(!bnetwork_initialized) + +#ifdef BADVPN_USE_WINAPI + + WORD requested = MAKEWORD(2, 2); + WSADATA wsadata; + if (WSAStartup(requested, &wsadata) != 0) { + BLog(BLOG_ERROR, "WSAStartup failed"); + goto fail0; + } + if (wsadata.wVersion != requested) { + BLog(BLOG_ERROR, "WSAStartup returned wrong version"); + goto fail1; + } + +#else + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGPIPE, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto fail0; + } + +#endif + + bnetwork_initialized = 1; + + return 1; + +#ifdef BADVPN_USE_WINAPI +fail1: + WSACleanup(); +#endif + +fail0: + return 0; +} + +void BNetwork_Assert (void) +{ + ASSERT(bnetwork_initialized) +} diff --git a/external/badvpn_dns/system/BNetwork.h b/external/badvpn_dns/system/BNetwork.h new file mode 100644 index 00000000..0db1744c --- /dev/null +++ b/external/badvpn_dns/system/BNetwork.h @@ -0,0 +1,36 @@ +/** + * @file BNetwork.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BNETWORK_H +#define BADVPN_SYSTEM_BNETWORK_H + +int BNetwork_GlobalInit (void); +void BNetwork_Assert (void); + +#endif diff --git a/external/badvpn_dns/system/BProcess.c b/external/badvpn_dns/system/BProcess.c new file mode 100644 index 00000000..7efea258 --- /dev/null +++ b/external/badvpn_dns/system/BProcess.c @@ -0,0 +1,400 @@ +/** + * @file BProcess.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "BProcess.h" + +#include + +static void call_handler (BProcess *o, int normally, uint8_t normally_exit_status) +{ + DEBUGERROR(&o->d_err, o->handler(o->user, normally, normally_exit_status)) +} + +static BProcess * find_process (BProcessManager *o, pid_t pid) +{ + for (LinkedList1Node *node = LinkedList1_GetFirst(&o->processes); node; node = LinkedList1Node_Next(node)) { + BProcess *p = UPPER_OBJECT(node, BProcess, list_node); + if (p->pid == pid) { + return p; + } + } + + return NULL; +} + +static void work_signals (BProcessManager *o) +{ + // read exit status with waitpid() + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) { + return; + } + + // schedule next waitpid + BPending_Set(&o->wait_job); + + // find process + BProcess *p = find_process(o, pid); + if (!p) { + BLog(BLOG_DEBUG, "unknown child %p"); + } + + if (WIFEXITED(status)) { + uint8_t exit_status = WEXITSTATUS(status); + + BLog(BLOG_INFO, "child %"PRIiMAX" exited with status %"PRIu8, (intmax_t)pid, exit_status); + + if (p) { + call_handler(p, 1, exit_status); + return; + } + } + else if (WIFSIGNALED(status)) { + int signo = WTERMSIG(status); + + BLog(BLOG_INFO, "child %"PRIiMAX" exited with signal %d", (intmax_t)pid, signo); + + if (p) { + call_handler(p, 0, 0); + return; + } + } + else { + BLog(BLOG_ERROR, "unknown wait status type for pid %"PRIiMAX" (%d)", (intmax_t)pid, status); + } +} + +static void wait_job_handler (BProcessManager *o) +{ + DebugObject_Access(&o->d_obj); + + work_signals(o); + return; +} + +static void signal_handler (BProcessManager *o, int signo) +{ + ASSERT(signo == SIGCHLD) + DebugObject_Access(&o->d_obj); + + work_signals(o); + return; +} + +int BProcessManager_Init (BProcessManager *o, BReactor *reactor) +{ + // init arguments + o->reactor = reactor; + + // init signal handling + sigset_t sset; + ASSERT_FORCE(sigemptyset(&sset) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGCHLD) == 0) + if (!BUnixSignal_Init(&o->signal, o->reactor, sset, (BUnixSignal_handler)signal_handler, o)) { + BLog(BLOG_ERROR, "BUnixSignal_Init failed"); + goto fail0; + } + + // init processes list + LinkedList1_Init(&o->processes); + + // init wait job + BPending_Init(&o->wait_job, BReactor_PendingGroup(o->reactor), (BPending_handler)wait_job_handler, o); + + DebugObject_Init(&o->d_obj); + + return 1; + +fail0: + return 0; +} + +void BProcessManager_Free (BProcessManager *o) +{ + ASSERT(LinkedList1_IsEmpty(&o->processes)) + DebugObject_Free(&o->d_obj); + + // free wait job + BPending_Free(&o->wait_job); + + // free signal handling + BUnixSignal_Free(&o->signal, 1); +} + +static int fds_contains (const int *fds, int fd, size_t *pos) +{ + for (size_t i = 0; fds[i] >= 0; i++) { + if (fds[i] == fd) { + if (pos) { + *pos = i; + } + + return 1; + } + } + + return 0; +} + +int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params) +{ + // init arguments + o->m = m; + o->handler = handler; + o->user = user; + + // count fds + size_t num_fds; + for (num_fds = 0; params.fds[num_fds] >= 0; num_fds++); + + // block signals + // needed to prevent parent's signal handlers from being called + // in the child + sigset_t sset_all; + sigfillset(&sset_all); + sigset_t sset_old; + if (sigprocmask(SIG_SETMASK, &sset_all, &sset_old) < 0) { + BLog(BLOG_ERROR, "sigprocmask failed"); + goto fail0; + } + + // fork + pid_t pid = fork(); + + if (pid == 0) { + // this is child + + // restore signal dispositions + for (int i = 1; i < NSIG; i++) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigaction(i, &sa, NULL); + } + + // unblock signals + sigset_t sset_none; + sigemptyset(&sset_none); + if (sigprocmask(SIG_SETMASK, &sset_none, NULL) < 0) { + abort(); + } + + // copy fds array + int *fds2 = malloc((num_fds + 1) * sizeof(fds2[0])); + if (!fds2) { + abort(); + } + memcpy(fds2, params.fds, (num_fds + 1) * sizeof(fds2[0])); + + // find maximum file descriptors + int max_fd = sysconf(_SC_OPEN_MAX); + if (max_fd < 0) { + abort(); + } + + // close file descriptors + for (int i = 0; i < max_fd; i++) { + // if it's one of the given fds, don't close it + if (fds_contains(fds2, i, NULL)) { + continue; + } + + close(i); + } + + // map fds to requested fd numbers + while (*fds2 >= 0) { + // resolve possible conflict + size_t cpos; + if (fds_contains(fds2 + 1, *params.fds_map, &cpos)) { + // dup() the fd to a new number; the old one will be closed + // in the following dup2() + if ((fds2[1 + cpos] = dup(fds2[1 + cpos])) < 0) { + abort(); + } + } + + if (*fds2 != *params.fds_map) { + // dup fd + if (dup2(*fds2, *params.fds_map) < 0) { + abort(); + } + + // close original fd + close(*fds2); + } + + fds2++; + params.fds_map++; + } + + // make sure standard streams are open + open_standard_streams(); + + // make session leader if requested + if (params.do_setsid) { + setsid(); + } + + // assume identity of username, if requested + if (params.username) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + abort(); + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(params.username, &pwd, buf, bufsize, &res); + if (!res) { + abort(); + } + + if (initgroups(params.username, pwd.pw_gid) < 0) { + abort(); + } + + if (setgid(pwd.pw_gid) < 0) { + abort(); + } + + if (setuid(pwd.pw_uid) < 0) { + abort(); + } + } + + execv(file, argv); + + abort(); + } + + // restore original signal mask + ASSERT_FORCE(sigprocmask(SIG_SETMASK, &sset_old, NULL) == 0) + + if (pid < 0) { + BLog(BLOG_ERROR, "fork failed"); + goto fail0; + } + + // remember pid + o->pid = pid; + + // add to processes list + LinkedList1_Append(&o->m->processes, &o->list_node); + + DebugObject_Init(&o->d_obj); + DebugError_Init(&o->d_err, BReactor_PendingGroup(m->reactor)); + + return 1; + +fail0: + return 0; +} + +int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map) +{ + struct BProcess_params params; + params.username = username; + params.fds = fds; + params.fds_map = fds_map; + params.do_setsid = 0; + + return BProcess_Init2(o, m, handler, user, file, argv, params); +} + +int BProcess_Init (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username) +{ + int fds[] = {-1}; + + return BProcess_InitWithFds(o, m, handler, user, file, argv, username, fds, NULL); +} + +void BProcess_Free (BProcess *o) +{ + DebugError_Free(&o->d_err); + DebugObject_Free(&o->d_obj); + + // remove from processes list + LinkedList1_Remove(&o->m->processes, &o->list_node); +} + +int BProcess_Terminate (BProcess *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + ASSERT(o->pid > 0) + + if (kill(o->pid, SIGTERM) < 0) { + BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGTERM) failed", (intmax_t)o->pid); + return 0; + } + + return 1; +} + +int BProcess_Kill (BProcess *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + ASSERT(o->pid > 0) + + if (kill(o->pid, SIGKILL) < 0) { + BLog(BLOG_ERROR, "kill(%"PRIiMAX", SIGKILL) failed", (intmax_t)o->pid); + return 0; + } + + return 1; +} diff --git a/external/badvpn_dns/system/BProcess.h b/external/badvpn_dns/system/BProcess.h new file mode 100644 index 00000000..35993fed --- /dev/null +++ b/external/badvpn_dns/system/BProcess.h @@ -0,0 +1,200 @@ +/** + * @file BProcess.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_BPROCESS_H +#define BADVPN_BPROCESS_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * Manages child processes. + * There may be at most one process manager at any given time. This restriction is not + * enforced, however. + */ +typedef struct { + BReactor *reactor; + BUnixSignal signal; + LinkedList1 processes; + BPending wait_job; + DebugObject d_obj; +} BProcessManager; + +/** + * Handler called when the process terminates. + * The process object must be freed from the job context of this handler. + * {@link BProcess_Terminate} or {@link BProcess_Kill} must not be called + * after this handler is called. + * + * @param user as in {@link BProcess_InitWithFds} or {@link BProcess_Init} + * @param normally whether the child process terminated normally (0 or 1) + * @param normally_exit_status if the child process terminated normally, its exit + * status; otherwise undefined + */ +typedef void (*BProcess_handler) (void *user, int normally, uint8_t normally_exit_status); + +/** + * Represents a child process. + */ +typedef struct { + BProcessManager *m; + BProcess_handler handler; + void *user; + pid_t pid; + LinkedList1Node list_node; // node in BProcessManager.processes + DebugObject d_obj; + DebugError d_err; +} BProcess; + +/** + * Initializes the process manager. + * There may be at most one process manager at any given time. This restriction is not + * enforced, however. + * + * @param o the object + * @param reactor reactor we live in + * @return 1 on success, 0 on failure + */ +int BProcessManager_Init (BProcessManager *o, BReactor *reactor) WARN_UNUSED; + +/** + * Frees the process manager. + * There must be no {@link BProcess} objects using this process manager. + * + * @param o the object + */ +void BProcessManager_Free (BProcessManager *o); + +struct BProcess_params { + const char *username; + const int *fds; + const int *fds_map; + int do_setsid; +}; + +/** + * Initializes the process. + * 'file', 'argv', 'username', 'fds' and 'fds_map' arguments are only used during this + * function call. + * If no file descriptor is mapped to a standard stream (file descriptors 0, 1, 2), + * then /dev/null will be opened in the child for that standard stream. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param params.username user account to run the program as, or NULL to not switch user + * @param params.fds array of file descriptors in the parent to map to file descriptors in the child, + * terminated with -1 + * @param params.fds_map array of file descriptors in the child that file descriptors in 'fds' will + * be mapped to, in the same order. Must contain the same number of file descriptors + * as the 'fds' argument, and does not have to be terminated with -1. + * @param params.do_setsid if set to non-zero, will make the child call setsid() before exec'ing. + * Failure of setsid() will be ignored. + * @return 1 on success, 0 on failure + */ +int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params) WARN_UNUSED; + +/** + * Initializes the process. + * 'file', 'argv', 'username', 'fds' and 'fds_map' arguments are only used during this + * function call. + * If no file descriptor is mapped to a standard stream (file descriptors 0, 1, 2), + * then /dev/null will be opened in the child for that standard stream. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param username user account to run the program as, or NULL to not switch user + * @param fds array of file descriptors in the parent to map to file descriptors in the child, + * terminated with -1 + * @param fds_map array of file descriptors in the child that file descriptors in 'fds' will + * be mapped to, in the same order. Must contain the same number of file descriptors + * as the 'fds' argument, and does not have to be terminated with -1. + * @return 1 on success, 0 on failure + */ +int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map) WARN_UNUSED; + +/** + * Initializes the process. + * Like {@link BProcess_InitWithFds}, but without file descriptor mapping. + * 'file', 'argv' and 'username' arguments are only used during this function call. + * + * @param o the object + * @param m process manager + * @param handler handler called when the process terminates + * @param user argument to handler + * @param file path to executable file + * @param argv arguments array, including the zeroth argument, terminated with a NULL pointer + * @param username user account to run the program as, or NULL to not switch user + * @return 1 on success, 0 on failure + */ +int BProcess_Init (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username) WARN_UNUSED; + +/** + * Frees the process. + * This does not do anything with the actual child process; it only prevents the user to wait + * for its termination. If the process terminates while a process manager is running, it will still + * be waited for (and will not become a zombie). + * + * @param o the object + */ +void BProcess_Free (BProcess *o); + +/** + * Sends the process the SIGTERM signal. + * Success of this action does NOT mean that the child has terminated. + * + * @param o the object + * @return 1 on success, 0 on failure + */ +int BProcess_Terminate (BProcess *o); + +/** + * Sends the process the SIGKILL signal. + * Success of this action does NOT mean that the child has terminated. + * + * @param o the object + * @return 1 on success, 0 on failure + */ +int BProcess_Kill (BProcess *o); + +#endif diff --git a/external/badvpn_dns/system/BReactor.h b/external/badvpn_dns/system/BReactor.h new file mode 100644 index 00000000..4c0fa5be --- /dev/null +++ b/external/badvpn_dns/system/BReactor.h @@ -0,0 +1,11 @@ +#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) + defined(BADVPN_BREACTOR_EMSCRIPTEN) != 1 +#error No reactor backend or too many reactor backens +#endif + +#if defined(BADVPN_BREACTOR_BADVPN) +#include "BReactor_badvpn.h" +#elif defined(BADVPN_BREACTOR_GLIB) +#include "BReactor_glib.h" +#elif defined(BADVPN_BREACTOR_EMSCRIPTEN) +#include "BReactor_emscripten.h" +#endif diff --git a/external/badvpn_dns/system/BReactor_badvpn.c b/external/badvpn_dns/system/BReactor_badvpn.c new file mode 100644 index 00000000..1b03d58d --- /dev/null +++ b/external/badvpn_dns/system/BReactor_badvpn.c @@ -0,0 +1,1430 @@ +/** + * @file BReactor_badvpn.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include + +#define KEVENT_TAG_FD 1 +#define KEVENT_TAG_KEVENT 2 + +#define TIMER_STATE_INACTIVE 1 +#define TIMER_STATE_RUNNING 2 +#define TIMER_STATE_EXPIRED 3 + +static int compare_timers (BSmallTimer *t1, BSmallTimer *t2) +{ + int cmp = B_COMPARE(t1->absTime, t2->absTime); + if (cmp) { + return cmp; + } + + return B_COMPARE((uintptr_t)t1, (uintptr_t)t2); +} + +#include "BReactor_badvpn_timerstree.h" +#include + +static void assert_timer (BSmallTimer *bt) +{ + ASSERT(bt->state == TIMER_STATE_INACTIVE || bt->state == TIMER_STATE_RUNNING || + bt->state == TIMER_STATE_EXPIRED) +} + +static int move_expired_timers (BReactor *bsys, btime_t now) +{ + int moved = 0; + + // move timed out timers to the expired list + BReactor__TimersTreeRef ref; + BSmallTimer *timer; + while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + + // if it's in the future, stop + if (timer->absTime > now) { + break; + } + moved = 1; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } + + return moved; +} + +static void move_first_timers (BReactor *bsys) +{ + BReactor__TimersTreeRef ref; + + // get the time of the first timer + BSmallTimer *first_timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link; + ASSERT(first_timer) + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + btime_t first_time = first_timer->absTime; + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &first_timer->u.list_node); + + // set expired + first_timer->state = TIMER_STATE_EXPIRED; + + // also move other timers with the same timeout + BSmallTimer *timer; + while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) { + ASSERT(timer->state == TIMER_STATE_RUNNING) + ASSERT(timer->absTime >= first_time) + + // if it's in the future, stop + if (timer->absTime > first_time) { + break; + } + + // remove from running timers tree + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + + // add to expired timers list + LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node); + + // set expired + timer->state = TIMER_STATE_EXPIRED; + } +} + +#ifdef BADVPN_USE_WINAPI + +static void set_iocp_ready (BReactorIOCPOverlapped *olap, int succeeded, DWORD bytes) +{ + BReactor *reactor = olap->reactor; + ASSERT(!olap->is_ready) + + // set parameters + olap->ready_succeeded = succeeded; + olap->ready_bytes = bytes; + + // insert to IOCP ready list + LinkedList1_Append(&reactor->iocp_ready_list, &olap->ready_list_node); + + // set ready + olap->is_ready = 1; +} + +#endif + +#ifdef BADVPN_USE_EPOLL + +static void set_epoll_fd_pointers (BReactor *bsys) +{ + // Write pointers to our entry pointers into file descriptors. + // If a handler function frees some other file descriptor, the + // free routine will set our pointer to NULL so we don't dispatch it. + for (int i = 0; i < bsys->epoll_results_num; i++) { + struct epoll_event *event = &bsys->epoll_results[i]; + ASSERT(event->data.ptr) + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(!bfd->epoll_returned_ptr) + bfd->epoll_returned_ptr = (BFileDescriptor **)&event->data.ptr; + } +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void set_kevent_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->kevent_results_num; i++) { + struct kevent *event = &bsys->kevent_results[i]; + ASSERT(event->udata) + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(!bfd->kevent_returned_ptr) + bfd->kevent_returned_ptr = (int **)&event->udata; + } break; + + case KEVENT_TAG_KEVENT: { + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(!kev->kevent_returned_ptr) + kev->kevent_returned_ptr = (int **)&event->udata; + } break; + + default: + ASSERT(0); + } + } +} + +static void update_kevent_fd_events (BReactor *bsys, BFileDescriptor *bs, int events) +{ + struct kevent event; + + if (!(bs->waitEvents & BREACTOR_READ) && (events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_READ) && !(events & BREACTOR_READ)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_READ; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + + if (!(bs->waitEvents & BREACTOR_WRITE) && (events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_ADD; + event.udata = &bs->kevent_tag; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } + else if ((bs->waitEvents & BREACTOR_WRITE) && !(events & BREACTOR_WRITE)) { + memset(&event, 0, sizeof(event)); + event.ident = bs->fd; + event.filter = EVFILT_WRITE; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) + } +} + +#endif + +#ifdef BADVPN_USE_POLL + +static void set_poll_fd_pointers (BReactor *bsys) +{ + for (int i = 0; i < bsys->poll_results_num; i++) { + BFileDescriptor *bfd = bsys->poll_results_bfds[i]; + ASSERT(bfd) + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + bfd->poll_returned_index = i; + } +} + +#endif + +static void wait_for_events (BReactor *bsys) +{ + // must have processed all pending events + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + #endif + #ifdef BADVPN_USE_EPOLL + ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num) + #endif + #ifdef BADVPN_USE_KEVENT + ASSERT(bsys->kevent_results_pos == bsys->kevent_results_num) + #endif + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_results_pos == bsys->poll_results_num) + #endif + + // clean up epoll results + #ifdef BADVPN_USE_EPOLL + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + #endif + + // clean up kevent results + #ifdef BADVPN_USE_KEVENT + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + #endif + + // clean up poll results + #ifdef BADVPN_USE_POLL + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + #endif + + // timeout vars + int have_timeout = 0; + btime_t timeout_abs; + btime_t now = 0; // to remove warning + + // compute timeout + BSmallTimer *first_timer = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0).link; + if (first_timer) { + ASSERT(first_timer->state == TIMER_STATE_RUNNING) + + // get current time + now = btime_gettime(); + + // if some timers have already timed out, return them immediately + if (move_expired_timers(bsys, now)) { + BLog(BLOG_DEBUG, "Got already expired timers"); + return; + } + + // timeout is first timer, remember absolute time + have_timeout = 1; + timeout_abs = first_timer->absTime; + } + + // wait until the timeout is reached or the file descriptor / handle in ready + while (1) { + // compute timeout + btime_t timeout_rel = 0; // to remove warning + btime_t timeout_rel_trunc = 0; // to remove warning + if (have_timeout) { + timeout_rel = timeout_abs - now; + timeout_rel_trunc = timeout_rel; + } + + // perform wait + + #ifdef BADVPN_USE_WINAPI + + if (have_timeout) { + if (timeout_rel_trunc > INFINITE - 1) { + timeout_rel_trunc = INFINITE - 1; + } + } + + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(bsys->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, (have_timeout ? timeout_rel_trunc : INFINITE)); + + ASSERT_FORCE(olap || have_timeout) + + if (olap || timeout_rel_trunc == timeout_rel) { + if (olap) { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus returned event"); + + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == bsys) + ASSERT(!olap->is_ready) + + set_iocp_ready(olap, (res == TRUE), bytes); + } else { + BLog(BLOG_DEBUG, "GetQueuedCompletionStatus timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + BLog(BLOG_DEBUG, "Calling epoll_wait"); + + int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "epoll_wait interrupted"); + goto try_again; + } + perror("epoll_wait"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "epoll_wait returned %d file descriptors", waitres); + bsys->epoll_results_num = waitres; + set_epoll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "epoll_wait timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + struct timespec ts; + if (have_timeout) { + if (timeout_rel_trunc > 86400000) { + timeout_rel_trunc = 86400000; + } + ts.tv_sec = timeout_rel_trunc / 1000; + ts.tv_nsec = (timeout_rel_trunc % 1000) * 1000000; + } + + BLog(BLOG_DEBUG, "Calling kevent"); + + int waitres = kevent(bsys->kqueue_fd, NULL, 0, bsys->kevent_results, BSYSTEM_MAX_RESULTS, (have_timeout ? &ts : NULL)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "kevent interrupted"); + goto try_again; + } + perror("kevent"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "kevent returned %d events", waitres); + bsys->kevent_results_num = waitres; + set_kevent_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "kevent timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (have_timeout) { + if (timeout_rel_trunc > INT_MAX) { + timeout_rel_trunc = INT_MAX; + } + } + + ASSERT(bsys->poll_num_enabled_fds >= 0) + ASSERT(bsys->poll_num_enabled_fds <= BSYSTEM_MAX_POLL_FDS) + int num_fds = 0; + + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->poll_enabled_fds_list); + while (list_node) { + BFileDescriptor *bfd = UPPER_OBJECT(list_node, BFileDescriptor, poll_enabled_fds_list_node); + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == -1) + + // calculate poll events + int pevents = 0; + if ((bfd->waitEvents & BREACTOR_READ)) { + pevents |= POLLIN; + } + if ((bfd->waitEvents & BREACTOR_WRITE)) { + pevents |= POLLOUT; + } + + // write pollfd entry + struct pollfd *pfd = &bsys->poll_results_pollfds[num_fds]; + pfd->fd = bfd->fd; + pfd->events = pevents; + pfd->revents = 0; + + // write BFileDescriptor reference entry + bsys->poll_results_bfds[num_fds] = bfd; + + // increment number of fds in array + num_fds++; + + list_node = LinkedList1Node_Next(list_node); + } + + BLog(BLOG_DEBUG, "Calling poll"); + + int waitres = poll(bsys->poll_results_pollfds, num_fds, (have_timeout ? timeout_rel_trunc : -1)); + if (waitres < 0) { + int error = errno; + if (error == EINTR) { + BLog(BLOG_DEBUG, "poll interrupted"); + goto try_again; + } + perror("poll"); + ASSERT_FORCE(0) + } + + ASSERT_FORCE(!(waitres == 0) || have_timeout) + + if (waitres != 0 || timeout_rel_trunc == timeout_rel) { + if (waitres != 0) { + BLog(BLOG_DEBUG, "poll returned %d file descriptors", waitres); + bsys->poll_results_num = num_fds; + bsys->poll_results_pos = 0; + set_poll_fd_pointers(bsys); + } else { + BLog(BLOG_DEBUG, "poll timed out"); + move_first_timers(bsys); + } + break; + } + + #endif + + try_again: + if (have_timeout) { + // get current time + now = btime_gettime(); + // check if we already reached the time we're waiting for + if (now >= timeout_abs) { + BLog(BLOG_DEBUG, "already timed out while trying again"); + move_first_timers(bsys); + break; + } + } + } + + // reset limit objects + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&bsys->active_limits_list)) { + BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node); + ASSERT(limit->count > 0) + limit->count = 0; + LinkedList1_Remove(&bsys->active_limits_list, &limit->active_limits_list_node); + } +} + +#ifndef BADVPN_USE_WINAPI + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user) +{ + bs->fd = fd; + bs->handler = handler; + bs->user = user; + bs->active = 0; +} + +#endif + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + bt->handler.smalll = handler; + bt->state = TIMER_STATE_INACTIVE; + bt->is_small = 1; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + assert_timer(bt); + + return (bt->state != TIMER_STATE_INACTIVE); +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->base.handler.heavy = handler; + bt->base.state = TIMER_STATE_INACTIVE; + bt->base.is_small = 0; + bt->user = user; + bt->msTime = msTime; +} + +int BTimer_IsRunning (BTimer *bt) +{ + return BSmallTimer_IsRunning(&bt->base); +} + +int BReactor_Init (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Reactor initializing"); + + // set not exiting + bsys->exiting = 0; + + // init jobs + BPendingGroup_Init(&bsys->pending_jobs); + + // init timers + BReactor__TimersTree_Init(&bsys->timers_tree); + LinkedList1_Init(&bsys->timers_expired_list); + + // init limits + LinkedList1_Init(&bsys->active_limits_list); + + #ifdef BADVPN_USE_WINAPI + + // init IOCP list + LinkedList1_Init(&bsys->iocp_list); + + // init IOCP handle + if (!(bsys->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1))) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail0; + } + + // init IOCP ready list + LinkedList1_Init(&bsys->iocp_ready_list); + + #endif + + #ifdef BADVPN_USE_EPOLL + + // create epoll fd + if ((bsys->efd = epoll_create(10)) < 0) { + BLog(BLOG_ERROR, "epoll_create failed"); + goto fail0; + } + + // init results array + bsys->epoll_results_num = 0; + bsys->epoll_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // create kqueue fd + if ((bsys->kqueue_fd = kqueue()) < 0) { + BLog(BLOG_ERROR, "kqueue failed"); + goto fail0; + } + + // init results array + bsys->kevent_results_num = 0; + bsys->kevent_results_pos = 0; + + #endif + + #ifdef BADVPN_USE_POLL + + // init enabled fds list + LinkedList1_Init(&bsys->poll_enabled_fds_list); + + // set zero enabled fds + bsys->poll_num_enabled_fds = 0; + + // allocate results arrays + if (!(bsys->poll_results_pollfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_pollfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + if (!(bsys->poll_results_bfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_bfds[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail1; + } + + // init results array + bsys->poll_results_num = 0; + bsys->poll_results_pos = 0; + + #endif + + DebugObject_Init(&bsys->d_obj); + #ifndef BADVPN_USE_WINAPI + DebugCounter_Init(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Init(&bsys->d_kevent_ctr); + #endif + DebugCounter_Init(&bsys->d_limits_ctr); + + return 1; + + #ifdef BADVPN_USE_POLL +fail1: + BFree(bsys->poll_results_pollfds); + #endif +fail0: + BPendingGroup_Free(&bsys->pending_jobs); + BLog(BLOG_ERROR, "Reactor failed to initialize"); + return 0; +} + +void BReactor_Free (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + #ifdef BADVPN_USE_WINAPI + while (!LinkedList1_IsEmpty(&bsys->iocp_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetLast(&bsys->iocp_list), BReactorIOCPOverlapped, iocp_list_node); + ASSERT(olap->reactor == bsys) + olap->handler(olap->user, BREACTOR_IOCP_EVENT_EXITING, 0); + } + #endif + + // {pending group has no BPending objects} + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(BReactor__TimersTree_IsEmpty(&bsys->timers_tree)) + ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list)) + DebugObject_Free(&bsys->d_obj); + #ifdef BADVPN_USE_WINAPI + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list)) + ASSERT(LinkedList1_IsEmpty(&bsys->iocp_list)) + #endif + #ifndef BADVPN_USE_WINAPI + DebugCounter_Free(&bsys->d_fds_counter); + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter_Free(&bsys->d_kevent_ctr); + #endif + DebugCounter_Free(&bsys->d_limits_ctr); + #ifdef BADVPN_USE_POLL + ASSERT(bsys->poll_num_enabled_fds == 0) + ASSERT(LinkedList1_IsEmpty(&bsys->poll_enabled_fds_list)) + #endif + + BLog(BLOG_DEBUG, "Reactor freeing"); + + #ifdef BADVPN_USE_WINAPI + + // close IOCP handle + ASSERT_FORCE(CloseHandle(bsys->iocp_handle)) + + #endif + + #ifdef BADVPN_USE_EPOLL + + // close epoll fd + ASSERT_FORCE(close(bsys->efd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + // close kqueue fd + ASSERT_FORCE(close(bsys->kqueue_fd) == 0) + + #endif + + #ifdef BADVPN_USE_POLL + + // free results arrays + BFree(bsys->poll_results_bfds); + BFree(bsys->poll_results_pollfds); + + #endif + + // free jobs + BPendingGroup_Free(&bsys->pending_jobs); +} + +int BReactor_Exec (BReactor *bsys) +{ + BLog(BLOG_DEBUG, "Entering event loop"); + + while (!bsys->exiting) { + // dispatch job + if (BPendingGroup_HasJobs(&bsys->pending_jobs)) { + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + continue; + } + + // dispatch timer + LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->timers_expired_list); + if (list_node) { + BSmallTimer *timer = UPPER_OBJECT(list_node, BSmallTimer, u.list_node); + ASSERT(timer->state == TIMER_STATE_EXPIRED) + + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &timer->u.list_node); + + // set inactive + timer->state = TIMER_STATE_INACTIVE; + + // call handler + BLog(BLOG_DEBUG, "Dispatching timer"); + if (timer->is_small) { + timer->handler.smalll(timer); + } else { + BTimer *btimer = UPPER_OBJECT(timer, BTimer, base); + timer->handler.heavy(btimer->user); + } + continue; + } + + #ifdef BADVPN_USE_WINAPI + + if (!LinkedList1_IsEmpty(&bsys->iocp_ready_list)) { + BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetFirst(&bsys->iocp_ready_list), BReactorIOCPOverlapped, ready_list_node); + ASSERT(olap->is_ready) + ASSERT(olap->handler) + + // remove from ready list + LinkedList1_Remove(&bsys->iocp_ready_list, &olap->ready_list_node); + + // set not ready + olap->is_ready = 0; + + int event = (olap->ready_succeeded ? BREACTOR_IOCP_EVENT_SUCCEEDED : BREACTOR_IOCP_EVENT_FAILED); + + // call handler + olap->handler(olap->user, event, olap->ready_bytes); + continue; + } + + #endif + + #ifdef BADVPN_USE_EPOLL + + // dispatch file descriptor + if (bsys->epoll_results_pos < bsys->epoll_results_num) { + // grab event + struct epoll_event *event = &bsys->epoll_results[bsys->epoll_results_pos]; + bsys->epoll_results_pos++; + + // check if the BFileDescriptor was removed + if (!event->data.ptr) { + continue; + } + + // get BFileDescriptor + BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr; + ASSERT(bfd->active) + ASSERT(bfd->epoll_returned_ptr == (BFileDescriptor **)&event->data.ptr) + + // zero pointer to the epoll entry + bfd->epoll_returned_ptr = NULL; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && (event->events&EPOLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && (event->events&EPOLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((event->events&EPOLLERR)) { + events |= BREACTOR_ERROR; + } + if ((event->events&EPOLLHUP)) { + events |= BREACTOR_HUP; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // dispatch kevent + if (bsys->kevent_results_pos < bsys->kevent_results_num) { + // grab event + struct kevent *event = &bsys->kevent_results[bsys->kevent_results_pos]; + bsys->kevent_results_pos++; + + // check if the event was removed + if (!event->udata) { + continue; + } + + // check tag + int *tag = event->udata; + switch (*tag) { + case KEVENT_TAG_FD: { + // get BFileDescriptor + BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag); + ASSERT(bfd->active) + ASSERT(bfd->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + bfd->kevent_returned_ptr = NULL; + + // calculate event to report + int events = 0; + if ((bfd->waitEvents&BREACTOR_READ) && event->filter == EVFILT_READ) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents&BREACTOR_WRITE) && event->filter == EVFILT_WRITE) { + events |= BREACTOR_WRITE; + } + + if (!events) { + BLog(BLOG_ERROR, "no events detected?"); + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } break; + + case KEVENT_TAG_KEVENT: { + // get BReactorKEvent + BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag); + ASSERT(kev->reactor == bsys) + ASSERT(kev->kevent_returned_ptr == (int **)&event->udata) + + // zero pointer to the kevent entry + kev->kevent_returned_ptr = NULL; + + // call handler + BLog(BLOG_DEBUG, "Dispatching kevent"); + kev->handler(kev->user, event->fflags, event->data); + continue; + } break; + + default: + ASSERT(0); + } + } + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_results_pos < bsys->poll_results_num) { + // grab event + struct pollfd *pfd = &bsys->poll_results_pollfds[bsys->poll_results_pos]; + BFileDescriptor *bfd = bsys->poll_results_bfds[bsys->poll_results_pos]; + bsys->poll_results_pos++; + + // skip removed entry + if (!bfd) { + continue; + } + + ASSERT(bfd->active) + ASSERT(bfd->poll_returned_index == bsys->poll_results_pos - 1) + + // remove result reference + bfd->poll_returned_index = -1; + + // calculate events to report + int events = 0; + if ((bfd->waitEvents & BREACTOR_READ) && (pfd->revents & POLLIN)) { + events |= BREACTOR_READ; + } + if ((bfd->waitEvents & BREACTOR_WRITE) && (pfd->revents & POLLOUT)) { + events |= BREACTOR_WRITE; + } + if ((pfd->revents & POLLERR) || (pfd->revents & POLLHUP)) { + events |= BREACTOR_ERROR; + } + + if (!events) { + continue; + } + + // call handler + BLog(BLOG_DEBUG, "Dispatching file descriptor"); + bfd->handler(bfd->user, events); + continue; + } + + #endif + + wait_for_events(bsys); + } + + BLog(BLOG_DEBUG, "Exiting event loop, exit code %d", bsys->exit_code); + + return bsys->exit_code; +} + +void BReactor_Quit (BReactor *bsys, int code) +{ + bsys->exiting = 1; + bsys->exit_code = code; +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + assert_timer(bt); + ASSERT(mode == BTIMER_SET_ABSOLUTE || mode == BTIMER_SET_RELATIVE) + + // unlink it if it's already in the list + BReactor_RemoveSmallTimer(bsys, bt); + + // if mode is relative, add current time + if (mode == BTIMER_SET_RELATIVE) { + time = btime_add(btime_gettime(), time); + } + + // set time + bt->absTime = time; + + // set running + bt->state = TIMER_STATE_RUNNING; + + // insert to running timers tree + BReactor__TimersTreeRef ref = {bt, bt}; + int res = BReactor__TimersTree_Insert(&bsys->timers_tree, 0, ref, NULL); + ASSERT_EXECUTE(res) +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + assert_timer(bt); + + if (bt->state == TIMER_STATE_INACTIVE) { + return; + } + + if (bt->state == TIMER_STATE_EXPIRED) { + // remove from expired list + LinkedList1_Remove(&bsys->timers_expired_list, &bt->u.list_node); + } else { + // remove from running tree + BReactor__TimersTreeRef ref = {bt, bt}; + BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref); + } + + // set inactive + bt->state = TIMER_STATE_INACTIVE; +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after); +} + +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time); +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + return BReactor_RemoveSmallTimer(bsys, &bt->base); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + return &bsys->pending_jobs; +} + +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref) +{ + ASSERT(ref) + + while (!bsys->exiting) { + ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs)) + + if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) { + return 1; + } + + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + } + + return 0; +} + +#ifndef BADVPN_USE_WINAPI + +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(!bs->active) + + #ifdef BADVPN_USE_EPOLL + + // add epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = 0; + event.data.ptr = bs; + if (epoll_ctl(bsys->efd, EPOLL_CTL_ADD, bs->fd, &event) < 0) { + int error = errno; + BLog(BLOG_ERROR, "epoll_ctl failed: %d", error); + return 0; + } + + // set epoll returned pointer + bs->epoll_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_KEVENT + + // set kevent tag + bs->kevent_tag = KEVENT_TAG_FD; + + // set kevent returned pointer + bs->kevent_returned_ptr = NULL; + + #endif + + #ifdef BADVPN_USE_POLL + + if (bsys->poll_num_enabled_fds == BSYSTEM_MAX_POLL_FDS) { + BLog(BLOG_ERROR, "too many fds"); + return 0; + } + + // append to enabled fds list + LinkedList1_Append(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds++; + + // set not returned + bs->poll_returned_index = -1; + + #endif + + bs->active = 1; + bs->waitEvents = 0; + + DebugCounter_Increment(&bsys->d_fds_counter); + return 1; +} + +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + ASSERT(bs->active) + DebugCounter_Decrement(&bsys->d_fds_counter); + + bs->active = 0; + + #ifdef BADVPN_USE_EPOLL + + // delete epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_DEL, bs->fd, &event) == 0) + + // write through epoll returned pointer + if (bs->epoll_returned_ptr) { + *bs->epoll_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // delete kevents + update_kevent_fd_events(bsys, bs, 0); + + // write through kevent returned pointer + if (bs->kevent_returned_ptr) { + *bs->kevent_returned_ptr = NULL; + } + + #endif + + #ifdef BADVPN_USE_POLL + + // invalidate results entry + if (bs->poll_returned_index != -1) { + ASSERT(bs->poll_returned_index >= bsys->poll_results_pos) + ASSERT(bs->poll_returned_index < bsys->poll_results_num) + ASSERT(bsys->poll_results_bfds[bs->poll_returned_index] == bs) + + bsys->poll_results_bfds[bs->poll_returned_index] = NULL; + } + + // remove from enabled fds list + LinkedList1_Remove(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node); + bsys->poll_num_enabled_fds--; + + #endif +} + +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events) +{ + ASSERT(bs->active) + ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE))) + + if (bs->waitEvents == events) { + return; + } + + #ifdef BADVPN_USE_EPOLL + + // calculate epoll events + int eevents = 0; + if ((events & BREACTOR_READ)) { + eevents |= EPOLLIN; + } + if ((events & BREACTOR_WRITE)) { + eevents |= EPOLLOUT; + } + + // update epoll entry + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = eevents; + event.data.ptr = bs; + ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_MOD, bs->fd, &event) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + update_kevent_fd_events(bsys, bs, events); + + #endif + + // update events + bs->waitEvents = events; +} + +#endif + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit) +{ + DebugObject_Access(&reactor->d_obj); + ASSERT(limit > 0) + + // init arguments + o->reactor = reactor; + o->limit = limit; + + // set count zero + o->count = 0; + + DebugCounter_Increment(&reactor->d_limits_ctr); + DebugObject_Init(&o->d_obj); +} + +void BReactorLimit_Free (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&reactor->d_limits_ctr); + + // remove from active limits list + if (o->count > 0) { + LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node); + } +} + +int BReactorLimit_Increment (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // check count against limit + if (o->count >= o->limit) { + return 0; + } + + // increment count + o->count++; + + // if limit was zero, add to active limits list + if (o->count == 1) { + LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node); + } + + return 1; +} + +void BReactorLimit_SetLimit (BReactorLimit *o, int limit) +{ + DebugObject_Access(&o->d_obj); + ASSERT(limit > 0) + + // set limit + o->limit = limit; +} + +#ifdef BADVPN_USE_KEVENT + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->handler = handler; + o->user = user; + o->ident = ident; + o->filter = filter; + + // add kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_ADD; + event.fflags = fflags; + event.data = data; + event.udata = &o->kevent_tag; + if (kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) < 0) { + return 0; + } + + // set kevent tag + o->kevent_tag = KEVENT_TAG_KEVENT; + + // set kevent returned pointer + o->kevent_returned_ptr = NULL; + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&o->reactor->d_kevent_ctr); + return 1; +} + +void BReactorKEvent_Free (BReactorKEvent *o) +{ + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&o->reactor->d_kevent_ctr); + + // write through kevent returned pointer + if (o->kevent_returned_ptr) { + *o->kevent_returned_ptr = NULL; + } + + // delete kevent + struct kevent event; + memset(&event, 0, sizeof(event)); + event.ident = o->ident; + event.filter = o->filter; + event.flags = EV_DELETE; + ASSERT_FORCE(kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) == 0) +} + +#endif + +#ifdef BADVPN_USE_WINAPI + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor) +{ + DebugObject_Access(&reactor->d_obj); + + return reactor->iocp_handle; +} + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler) +{ + DebugObject_Access(&reactor->d_obj); + + // init arguments + o->reactor = reactor; + o->user = user; + o->handler = handler; + + // zero overlapped + memset(&o->olap, 0, sizeof(o->olap)); + + // append to IOCP list + LinkedList1_Append(&reactor->iocp_list, &o->iocp_list_node); + + // set not ready + o->is_ready = 0; + + DebugObject_Init(&o->d_obj); +} + +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + + // remove from IOCP ready list + if (o->is_ready) { + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + } + + // remove from IOCP list + LinkedList1_Remove(&reactor->iocp_list, &o->iocp_list_node); +} + +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // wait for IOCP events until we get an event for this olap + while (!o->is_ready) { + DWORD bytes = 0; + ULONG_PTR key; + BReactorIOCPOverlapped *olap = NULL; + BOOL res = GetQueuedCompletionStatus(reactor->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, INFINITE); + + ASSERT_FORCE(olap) + DebugObject_Access(&olap->d_obj); + ASSERT(olap->reactor == reactor) + + // regular I/O should be done synchronously, so we shoudln't ever get a second completion before an + // existing one is dispatched. If however PostQueuedCompletionStatus is being used to signal events, + // just discard any excess events. + if (!olap->is_ready) { + set_iocp_ready(olap, (res == TRUE), bytes); + } + } + + // remove from IOCP ready list + LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node); + + // set not ready + o->is_ready = 0; + + if (out_succeeded) { + *out_succeeded = o->ready_succeeded; + } + if (out_bytes) { + *out_bytes = o->ready_bytes; + } +} + +#endif diff --git a/external/badvpn_dns/system/BReactor_badvpn.h b/external/badvpn_dns/system/BReactor_badvpn.h new file mode 100644 index 00000000..2c5ab4c0 --- /dev/null +++ b/external/badvpn_dns/system/BReactor_badvpn.h @@ -0,0 +1,572 @@ +/** + * @file BReactor_badvpn.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_USE_EPOLL) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_POLL)) != 1 +#error Unknown event backend or too many event backends +#endif + +#ifdef BADVPN_USE_WINAPI +#include +#endif + +#ifdef BADVPN_USE_EPOLL +#include +#endif + +#ifdef BADVPN_USE_KEVENT +#include +#include +#include +#endif + +#ifdef BADVPN_USE_POLL +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +struct BSmallTimer_t; +typedef struct BSmallTimer_t *BReactor_timerstree_link; + +#include "BReactor_badvpn_timerstree.h" +#include + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param timer pointer to the timer. Use the {@link UPPER_OBJECT} macro + * to obtain the pointer to the containing structure. + */ +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +/** + * Handler function invoked when the timer expires. + * The timer was in running state. + * The timer enters not running state before this function is invoked. + * This function is being called from within the timer's previosly + * associated reactor. + * + * @param user value passed to {@link BTimer_Init} + */ +typedef void (*BTimer_handler) (void *user); + +/** + * Timer object used with {@link BReactor}. + */ +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + union { + LinkedList1Node list_node; + struct BSmallTimer_t *tree_child[2]; + } u; + struct BSmallTimer_t *tree_parent; + btime_t absTime; + int8_t tree_balance; + uint8_t state; + uint8_t is_small; +} BSmallTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param handler handler function invoked when the timer expires + */ +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BSmallTimer_IsRunning (BSmallTimer *bt); + +/** + * Timer object used with {@link BReactor}. This is a legacy wrapper + * around {@link BSmallTimer} with an extra field for the default time. + */ +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +/** + * Initializes the timer object. + * The timer object is initialized in not running state. + * + * @param bt the object + * @param msTime default timeout in milliseconds + * @param handler handler function invoked when the timer expires + * @param user value to pass to the handler function + */ +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); + +/** + * Checks if the timer is running. + * + * @param bt the object + * @return 1 if running, 0 if not running + */ +int BTimer_IsRunning (BTimer *bt); + +#ifndef BADVPN_USE_WINAPI + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +/** + * Handler function invoked by the reactor when one or more events are detected. + * The events argument will contain a subset of the monitored events (BREACTOR_READ, BREACTOR_WRITE), + * plus possibly the error event (BREACTOR_ERROR). + * The file descriptor object is in active state, being called from within + * the associated reactor. + * + * @param user value passed to {@link BFileDescriptor_Init} + * @param events bitmask composed of a subset of monitored events (BREACTOR_READ, BREACTOR_WRITE), + * and possibly the error event BREACTOR_ERROR and the hang-up event BREACTOR_HUP. + * Will be nonzero. + */ +typedef void (*BFileDescriptor_handler) (void *user, int events); + +/** + * File descriptor object used with {@link BReactor}. + */ +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + + #ifdef BADVPN_USE_EPOLL + struct BFileDescriptor_t **epoll_returned_ptr; + #endif + + #ifdef BADVPN_USE_KEVENT + int kevent_tag; + int **kevent_returned_ptr; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1Node poll_enabled_fds_list_node; + int poll_returned_index; + #endif +} BFileDescriptor; + +/** + * Intializes the file descriptor object. + * The object is initialized in not active state. + * + * @param bs file descriptor object to initialize + * @param fb file descriptor to represent + * @param handler handler function invoked by the reactor when a monitored event is detected + * @param user value passed to the handler functuon + */ +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +#endif + +// BReactor + +#define BSYSTEM_MAX_RESULTS 64 +#define BSYSTEM_MAX_HANDLES 64 +#define BSYSTEM_MAX_POLL_FDS 4096 + +/** + * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events + * and timers. + */ +typedef struct { + int exiting; + int exit_code; + + // jobs + BPendingGroup pending_jobs; + + // timers + BReactor__TimersTree timers_tree; + LinkedList1 timers_expired_list; + + // limits + LinkedList1 active_limits_list; + + #ifdef BADVPN_USE_WINAPI + LinkedList1 iocp_list; + HANDLE iocp_handle; + LinkedList1 iocp_ready_list; + #endif + + #ifdef BADVPN_USE_EPOLL + int efd; // epoll fd + struct epoll_event epoll_results[BSYSTEM_MAX_RESULTS]; // epoll returned events buffer + int epoll_results_num; // number of events in the array + int epoll_results_pos; // number of events processed so far + #endif + + #ifdef BADVPN_USE_KEVENT + int kqueue_fd; + struct kevent kevent_results[BSYSTEM_MAX_RESULTS]; + int kevent_results_num; + int kevent_results_pos; + #endif + + #ifdef BADVPN_USE_POLL + LinkedList1 poll_enabled_fds_list; + int poll_num_enabled_fds; + int poll_results_num; + int poll_results_pos; + struct pollfd *poll_results_pollfds; + BFileDescriptor **poll_results_bfds; + #endif + + DebugObject d_obj; + #ifndef BADVPN_USE_WINAPI + DebugCounter d_fds_counter; + #endif + #ifdef BADVPN_USE_KEVENT + DebugCounter d_kevent_ctr; + #endif + DebugCounter d_limits_ctr; +} BReactor; + +/** + * Initializes the reactor. + * {@link BLog_Init} must have been done. + * {@link BTime_Init} must have been done. + * + * @param bsys the object + * @return 1 on success, 0 on failure + */ +int BReactor_Init (BReactor *bsys) WARN_UNUSED; + +/** + * Frees the reactor. + * Must not be called from within the event loop ({@link BReactor_Exec}). + * There must be no {@link BPending} or {@link BSmallPending} objects using the + * pending group returned by {@link BReactor_PendingGroup}. + * There must be no running timers in this reactor. + * There must be no limit objects in this reactor. + * There must be no file descriptors or handles registered + * with this reactor. + * There must be no {@link BReactorKEvent} objects in this reactor. + * + * @param bsys the object + */ +void BReactor_Free (BReactor *bsys); + +/** + * Runs the event loop. + * + * @param bsys the object + * @return value passed to {@link BReactor_Quit} + */ +int BReactor_Exec (BReactor *bsys); + +/** + * Causes the event loop ({@link BReactor_Exec}) to cease + * dispatching events and return. + * Any further calls of {@link BReactor_Exec} will return immediately. + * + * @param bsys the object + * @param code value {@link BReactor_Exec} should return. If this is + * called more than once, it will return the last code. + */ +void BReactor_Quit (BReactor *bsys, int code); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BSmallTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param mode interpretation of time (BTIMER_SET_ABSOLUTE or BTIMER_SET_RELATIVE) + * @param time absolute or relative expiration time + */ +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +/** + * Starts a timer to expire after its default time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + */ +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); + +/** + * Starts a timer to expire after a given time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * + * @param bsys the object + * @param bt timer to start + * @param after relative expiration time + */ +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); + +/** + * Starts a timer to expire at the specified time. + * The timer must have been initialized with {@link BTimer_Init}. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters running state, associated with this reactor. + * The timer's expiration time is set to the time argument. + * + * @param bsys the object + * @param bt timer to start + * @param time absolute expiration time (according to {@link btime_gettime}) + */ +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); + +/** + * Stops a timer. + * If the timer is in running state, it must be associated with this reactor. + * The timer enters not running state. + * + * @param bsys the object + * @param bt timer to stop + */ +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +/** + * Returns a {@link BPendingGroup} object that can be used to schedule jobs for + * the reactor to execute. These jobs have complete priority over other events + * (timers, file descriptors and Windows handles). + * The returned pending group may only be used as an argument to {@link BPending_Init}, + * and must not be accessed by other means. + * All {@link BPending} and {@link BSmallPending} objects using this group must be + * freed before freeing the reactor. + * + * @param bsys the object + * @return pending group for scheduling jobs for the reactor to execute + */ +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +/** + * Executes pending jobs until either: + * - the reference job is reached, or + * - {@link BReactor_Quit} is called. + * The reference job must be reached before the job list empties. + * The reference job will not be executed. + * + * WARNING: Use with care. This should only be used to to work around third-party software + * that does not integrade into the jobs system. In particular, you should think about: + * - the effects the jobs to be executed may have, and + * - the environment those jobs expect to be executed in. + * + * @param bsys the object + * @param ref reference job. It is not accessed in any way, only its address is compared to + * pending jobs before they are executed. + * @return 1 if the reference job was reached, + * 0 if {@link BReactor_Quit} was called (either while executing a job, or before) + */ +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); + +#ifndef BADVPN_USE_WINAPI + +/** + * Starts monitoring a file descriptor. + * + * @param bsys the object + * @param bs file descriptor object. Must have been initialized with + * {@link BFileDescriptor_Init} Must be in not active state. + * On success, the file descriptor object enters active state, + * associated with this reactor. + * @return 1 on success, 0 on failure + */ +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; + +/** + * Stops monitoring a file descriptor. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. The file descriptor object + * enters not active state. + */ +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); + +/** + * Sets monitored file descriptor events. + * + * @param bsys the object + * @param bs {@link BFileDescriptor} object. Must be in active state, + * associated with this reactor. + * @param events events to watch for. Must not have any bits other than + * BREACTOR_READ and BREACTOR_WRITE. + * This overrides previosly monitored events. + */ +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +#endif + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +/** + * Initializes a limit object. + * A limit object consists of a counter integer, which is initialized to + * zero, is incremented by {@link BReactorLimit_Increment} up to \a limit, + * and is reset to zero every time the event loop performs a wait. + * If the event loop has processed all detected events, and before performing + * a wait, it determines that timers have expired, the counter will not be reset. + * + * @param o the object + * @param reactor reactor the object is tied to + * @param limit maximum counter value. Must be >0. + */ +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); + +/** + * Frees a limit object. + * + * @param o the object + */ +void BReactorLimit_Free (BReactorLimit *o); + +/** + * Attempts to increment the counter of a limit object. + * + * @param o the object + * @return 1 if the counter was lesser than the limit and was incremented, + * 0 if the counter was greater or equal to the limit and could not be + * incremented + */ +int BReactorLimit_Increment (BReactorLimit *o); + +/** + * Sets the limit of a limit object. + * + * @param o the object + * @param limit new limit. Must be >0. + */ +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#ifdef BADVPN_USE_KEVENT + +typedef void (*BReactorKEvent_handler) (void *user, u_int fflags, intptr_t data); + +typedef struct { + BReactor *reactor; + BReactorKEvent_handler handler; + void *user; + uintptr_t ident; + short filter; + int kevent_tag; + int **kevent_returned_ptr; + DebugObject d_obj; +} BReactorKEvent; + +int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data); +void BReactorKEvent_Free (BReactorKEvent *o); + +#endif + +#ifdef BADVPN_USE_WINAPI + +#define BREACTOR_IOCP_EVENT_SUCCEEDED 1 +#define BREACTOR_IOCP_EVENT_FAILED 2 +#define BREACTOR_IOCP_EVENT_EXITING 3 + +typedef void (*BReactorIOCPOverlapped_handler) (void *user, int event, DWORD bytes); + +typedef struct { + OVERLAPPED olap; + BReactor *reactor; + void *user; + BReactorIOCPOverlapped_handler handler; + LinkedList1Node iocp_list_node; + int is_ready; + LinkedList1Node ready_list_node; + int ready_succeeded; + DWORD ready_bytes; + DebugObject d_obj; +} BReactorIOCPOverlapped; + +HANDLE BReactor_GetIOCPHandle (BReactor *reactor); + +void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler); +void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o); +void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes); + +#endif + +#endif diff --git a/external/badvpn_dns/system/BReactor_badvpn_timerstree.h b/external/badvpn_dns/system/BReactor_badvpn_timerstree.h new file mode 100644 index 00000000..3cecd75e --- /dev/null +++ b/external/badvpn_dns/system/BReactor_badvpn_timerstree.h @@ -0,0 +1,13 @@ +#define CAVL_PARAM_NAME BReactor__TimersTree +#define CAVL_PARAM_FEATURE_COUNTS 0 +#define CAVL_PARAM_FEATURE_KEYS_ARE_INDICES 0 +#define CAVL_PARAM_FEATURE_NOKEYS 1 +#define CAVL_PARAM_TYPE_ENTRY struct BSmallTimer_t +#define CAVL_PARAM_TYPE_LINK BReactor_timerstree_link +#define CAVL_PARAM_TYPE_ARG int +#define CAVL_PARAM_VALUE_NULL ((BReactor_timerstree_link)NULL) +#define CAVL_PARAM_FUN_DEREF(arg, link) (link) +#define CAVL_PARAM_FUN_COMPARE_ENTRIES(arg, entry1, entry2) compare_timers((entry1).link, (entry2).link) +#define CAVL_PARAM_MEMBER_CHILD u.tree_child +#define CAVL_PARAM_MEMBER_BALANCE tree_balance +#define CAVL_PARAM_MEMBER_PARENT tree_parent diff --git a/external/badvpn_dns/system/BReactor_emscripten.c b/external/badvpn_dns/system/BReactor_emscripten.c new file mode 100644 index 00000000..f90af558 --- /dev/null +++ b/external/badvpn_dns/system/BReactor_emscripten.c @@ -0,0 +1,176 @@ +/** + * @file BReactor_emscripten.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include "BReactor_emscripten.h" + +#include + +#include + +static void assert_timer (BTimer *bt, BReactor *reactor) +{ + ASSERT(bt->active == 0 || bt->active == 1); + ASSERT(bt->active == 0 || bt->reactor); + ASSERT(bt->active == 0 || (!reactor || bt->reactor == reactor)); +} + +static void dispatch_pending (BReactor *o) +{ + while (BPendingGroup_HasJobs(&o->pending_jobs)) { + BPendingGroup_ExecuteJob(&o->pending_jobs); + } +} + +__attribute__((used)) +void breactor_timer_cb (BReactor *reactor, BTimer *bt) +{ + assert_timer(bt, reactor); + ASSERT(bt->active); + ASSERT(!BPendingGroup_HasJobs(&reactor->pending_jobs)); + + bt->active = 0; + + bt->handler(bt->handler_pointer); + dispatch_pending(reactor); +} + +static void small_timer_handler (void *vbt) +{ + BSmallTimer *bt = vbt; + + bt->handler(bt); +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->msTime = msTime; + bt->handler = handler; + bt->handler_pointer = user; + bt->active = 0; +} + +int BTimer_IsRunning (BTimer *bt) +{ + assert_timer(bt, NULL); + + return bt->active; +} + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + BTimer_Init(&bt->timer, 0, small_timer_handler, bt); + bt->handler = handler; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + return BTimer_IsRunning(&bt->timer); +} + +void BReactor_EmscriptenInit (BReactor *bsys) +{ + BPendingGroup_Init(&bsys->pending_jobs); + + DebugObject_Init(&bsys->d_obj); +} + +void BReactor_EmscriptenFree (BReactor *bsys) +{ + DebugObject_Free(&bsys->d_obj); + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)); +} + +void BReactor_EmscriptenSync (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + dispatch_pending(bsys); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return &bsys->pending_jobs; +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetTimerAfter(bsys, bt, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt, bsys); + + if (bt->active) { + BReactor_RemoveTimer(bsys, bt); + } + + char cmd[120]; + sprintf(cmd, "setTimeout(function(){Module.ccall('breactor_timer_cb','null',['number','number'],[%d,%d]);},%"PRIi64")", (int)bsys, (int)bt, after); + + bt->active = 1; + bt->timerid = emscripten_run_script_int(cmd); + bt->reactor = bsys; +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt, bsys); + + if (!bt->active) { + return; + } + + char cmd[30]; + sprintf(cmd, "clearTimeout(%d)", bt->timerid); + + emscripten_run_script(cmd); + bt->active = 0; +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + ASSERT(mode == BTIMER_SET_RELATIVE) + + BReactor_SetTimerAfter(bsys, &bt->timer, time); +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + BReactor_RemoveTimer(bsys, &bt->timer); +} diff --git a/external/badvpn_dns/system/BReactor_emscripten.h b/external/badvpn_dns/system/BReactor_emscripten.h new file mode 100644 index 00000000..c004d163 --- /dev/null +++ b/external/badvpn_dns/system/BReactor_emscripten.h @@ -0,0 +1,87 @@ +/** + * @file BReactor_emscripten.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include +#include +#include + +#define BTIMER_SET_RELATIVE 2 + +typedef struct BReactor_s BReactor; + +typedef void (*BTimer_handler) (void *user); + +typedef struct BTimer_t { + btime_t msTime; + BTimer_handler handler; + void *handler_pointer; + uint8_t active; + int timerid; + BReactor *reactor; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BSmallTimer_t; + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); + +typedef struct BSmallTimer_t { + BTimer timer; + BSmallTimer_handler handler; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +struct BReactor_s { + BPendingGroup pending_jobs; + DebugObject d_obj; +}; + +void BReactor_EmscriptenInit (BReactor *bsys); +void BReactor_EmscriptenFree (BReactor *bsys); +void BReactor_EmscriptenSync (BReactor *bsys); + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); + +#endif diff --git a/external/badvpn_dns/system/BReactor_glib.c b/external/badvpn_dns/system/BReactor_glib.c new file mode 100644 index 00000000..63f0fd6b --- /dev/null +++ b/external/badvpn_dns/system/BReactor_glib.c @@ -0,0 +1,524 @@ +/** + * @file BReactor_glib.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include "BReactor_glib.h" + +#include + +struct fd_source { + GSource source; + BFileDescriptor *bfd; +}; + +static void assert_timer (BSmallTimer *bt) +{ + ASSERT(bt->is_small == 0 || bt->is_small == 1) + ASSERT(bt->active == 0 || bt->active == 1) + ASSERT(!bt->active || bt->reactor) + ASSERT(!bt->active || bt->source) +} + +static void dispatch_pending (BReactor *o) +{ + while (!o->exiting && BPendingGroup_HasJobs(&o->pending_jobs)) { + BPendingGroup_ExecuteJob(&o->pending_jobs); + } +} + +static void reset_limits (BReactor *o) +{ + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->active_limits_list)) { + BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node); + ASSERT(limit->count > 0) + limit->count = 0; + LinkedList1_Remove(&o->active_limits_list, &limit->active_limits_list_node); + } +} + +static gushort get_glib_wait_events (int ev) +{ + gushort gev = G_IO_ERR | G_IO_HUP; + + if (ev & BREACTOR_READ) { + gev |= G_IO_IN; + } + + if (ev & BREACTOR_WRITE) { + gev |= G_IO_OUT; + } + + return gev; +} + +static int get_fd_dispatchable_events (BFileDescriptor *bfd) +{ + ASSERT(bfd->active) + + int ev = 0; + + if ((bfd->waitEvents & BREACTOR_READ) && (bfd->pollfd.revents & G_IO_IN)) { + ev |= BREACTOR_READ; + } + + if ((bfd->waitEvents & BREACTOR_WRITE) && (bfd->pollfd.revents & G_IO_OUT)) { + ev |= BREACTOR_WRITE; + } + + if ((bfd->pollfd.revents & G_IO_ERR)) { + ev |= BREACTOR_ERROR; + } + + if ((bfd->pollfd.revents & G_IO_HUP)) { + ev |= BREACTOR_HUP; + } + + return ev; +} + +static gboolean timer_source_handler (gpointer data) +{ + BSmallTimer *bt = (void *)data; + assert_timer(bt); + ASSERT(bt->active) + + BReactor *reactor = bt->reactor; + + if (reactor->exiting) { + return FALSE; + } + + g_source_destroy(bt->source); + g_source_unref(bt->source); + bt->active = 0; + DebugCounter_Decrement(&reactor->d_timers_ctr); + + if (bt->is_small) { + bt->handler.smalll(bt); + } else { + BTimer *btimer = UPPER_OBJECT(bt, BTimer, base); + bt->handler.heavy(btimer->user); + } + + dispatch_pending(reactor); + reset_limits(reactor); + + return FALSE; +} + +static gboolean fd_source_func_prepare (GSource *source, gint *timeout) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + *timeout = -1; + return FALSE; +} + +static gboolean fd_source_func_check (GSource *source) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + return (get_fd_dispatchable_events(bfd) ? TRUE : FALSE); +} + +static gboolean fd_source_func_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + BFileDescriptor *bfd = ((struct fd_source *)source)->bfd; + BReactor *reactor = bfd->reactor; + ASSERT(bfd->active) + ASSERT(bfd->source == source) + + if (reactor->exiting) { + return TRUE; + } + + int events = get_fd_dispatchable_events(bfd); + if (!events) { + return TRUE; + } + + bfd->handler(bfd->user, events); + dispatch_pending(reactor); + reset_limits(reactor); + + return TRUE; +} + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler) +{ + bt->handler.smalll = handler; + bt->active = 0; + bt->is_small = 1; +} + +int BSmallTimer_IsRunning (BSmallTimer *bt) +{ + assert_timer(bt); + + return bt->active; +} + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user) +{ + bt->base.handler.heavy = handler; + bt->base.active = 0; + bt->base.is_small = 0; + bt->user = user; + bt->msTime = msTime; +} + +int BTimer_IsRunning (BTimer *bt) +{ + return BSmallTimer_IsRunning(&bt->base); +} + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user) +{ + bs->fd = fd; + bs->handler = handler; + bs->user = user; + bs->active = 0; +} + +int BReactor_Init (BReactor *bsys) +{ + return BReactor_InitFromExistingGMainLoop(bsys, g_main_loop_new(NULL, FALSE), 1); +} + +void BReactor_Free (BReactor *bsys) +{ + DebugObject_Free(&bsys->d_obj); + DebugCounter_Free(&bsys->d_timers_ctr); + DebugCounter_Free(&bsys->d_limits_ctr); + DebugCounter_Free(&bsys->d_fds_counter); + ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs)) + ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list)) + + // free job queue + BPendingGroup_Free(&bsys->pending_jobs); + + // unref main loop if needed + if (bsys->unref_gloop_on_free) { + g_main_loop_unref(bsys->gloop); + } +} + +int BReactor_Exec (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + // dispatch pending jobs (until exiting) and reset limits + dispatch_pending(bsys); + reset_limits(bsys); + + // if exiting, do not enter glib loop + if (bsys->exiting) { + return bsys->exit_code; + } + + // enter glib loop + g_main_loop_run(bsys->gloop); + + ASSERT(bsys->exiting) + + return bsys->exit_code; +} + +void BReactor_Quit (BReactor *bsys, int code) +{ + DebugObject_Access(&bsys->d_obj); + + // remember exiting + bsys->exiting = 1; + bsys->exit_code = code; + + // request termination of glib loop + g_main_loop_quit(bsys->gloop); +} + +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt); + + // remove timer if it's already set + BReactor_RemoveSmallTimer(bsys, bt); + + // if mode is absolute, subtract current time + if (mode == BTIMER_SET_ABSOLUTE) { + btime_t now = btime_gettime(); + time = (time < now ? 0 : time - now); + } + + // set active and reactor + bt->active = 1; + bt->reactor = bsys; + + // init source + bt->source = g_timeout_source_new(time); + g_source_set_callback(bt->source, timer_source_handler, bt, NULL); + g_source_attach(bt->source, g_main_loop_get_context(bsys->gloop)); + + DebugCounter_Increment(&bsys->d_timers_ctr); +} + +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt) +{ + DebugObject_Access(&bsys->d_obj); + assert_timer(bt); + + // do nothing if timer is not active + if (!bt->active) { + return; + } + + // free source + g_source_destroy(bt->source); + g_source_unref(bt->source); + + // set not active + bt->active = 0; + + DebugCounter_Decrement(&bsys->d_timers_ctr); +} + +void BReactor_SetTimer (BReactor *bsys, BTimer *bt) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime); +} + +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after); +} + +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time) +{ + BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time); +} + +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt) +{ + return BReactor_RemoveSmallTimer(bsys, &bt->base); +} + +BPendingGroup * BReactor_PendingGroup (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return &bsys->pending_jobs; +} + +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(ref) + + while (!bsys->exiting) { + ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs)) + + if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) { + return 1; + } + + BPendingGroup_ExecuteJob(&bsys->pending_jobs); + } + + return 0; +} + +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(!bs->active) + + // set active, no wait events, and set reactor + bs->active = 1; + bs->waitEvents = 0; + bs->reactor = bsys; + + // create source + bs->source = g_source_new(&bsys->fd_source_funcs, sizeof(struct fd_source)); + ((struct fd_source *)bs->source)->bfd = bs; + + // init pollfd + bs->pollfd.fd = bs->fd; + bs->pollfd.events = get_glib_wait_events(bs->waitEvents); + bs->pollfd.revents = 0; + + // start source + g_source_add_poll(bs->source, &bs->pollfd); + g_source_attach(bs->source, g_main_loop_get_context(bsys->gloop)); + + DebugCounter_Increment(&bsys->d_fds_counter); + return 1; +} + +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs) +{ + DebugObject_Access(&bsys->d_obj); + DebugCounter_Decrement(&bsys->d_fds_counter); + ASSERT(bs->active) + + // free source + g_source_destroy(bs->source); + g_source_unref(bs->source); + + // set not active + bs->active = 0; +} + +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events) +{ + DebugObject_Access(&bsys->d_obj); + ASSERT(bs->active) + ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE))) + + // set new wait events + bs->waitEvents = events; + + // update pollfd wait events + bs->pollfd.events = get_glib_wait_events(bs->waitEvents); +} + +int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free) +{ + ASSERT(gloop) + ASSERT(unref_gloop_on_free == !!unref_gloop_on_free) + + // set not exiting + bsys->exiting = 0; + + // set gloop and unref on free flag + bsys->gloop = gloop; + bsys->unref_gloop_on_free = unref_gloop_on_free; + + // init fd source functions table + memset(&bsys->fd_source_funcs, 0, sizeof(bsys->fd_source_funcs)); + bsys->fd_source_funcs.prepare = fd_source_func_prepare; + bsys->fd_source_funcs.check = fd_source_func_check; + bsys->fd_source_funcs.dispatch = fd_source_func_dispatch; + bsys->fd_source_funcs.finalize = NULL; + + // init job queue + BPendingGroup_Init(&bsys->pending_jobs); + + // init active limits list + LinkedList1_Init(&bsys->active_limits_list); + + DebugCounter_Init(&bsys->d_fds_counter); + DebugCounter_Init(&bsys->d_limits_ctr); + DebugCounter_Init(&bsys->d_timers_ctr); + DebugObject_Init(&bsys->d_obj); + return 1; +} + +GMainLoop * BReactor_GetGMainLoop (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + return bsys->gloop; +} + +int BReactor_SynchronizeAll (BReactor *bsys) +{ + DebugObject_Access(&bsys->d_obj); + + dispatch_pending(bsys); + + return !bsys->exiting; +} + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit) +{ + DebugObject_Access(&reactor->d_obj); + ASSERT(limit > 0) + + // init arguments + o->reactor = reactor; + o->limit = limit; + + // set count zero + o->count = 0; + + DebugCounter_Increment(&reactor->d_limits_ctr); + DebugObject_Init(&o->d_obj); +} + +void BReactorLimit_Free (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&reactor->d_limits_ctr); + + // remove from active limits list + if (o->count > 0) { + LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node); + } +} + +int BReactorLimit_Increment (BReactorLimit *o) +{ + BReactor *reactor = o->reactor; + DebugObject_Access(&o->d_obj); + + // check count against limit + if (o->count >= o->limit) { + return 0; + } + + // increment count + o->count++; + + // if limit was zero, add to active limits list + if (o->count == 1) { + LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node); + } + + return 1; +} + +void BReactorLimit_SetLimit (BReactorLimit *o, int limit) +{ + DebugObject_Access(&o->d_obj); + ASSERT(limit > 0) + + // set limit + o->limit = limit; +} diff --git a/external/badvpn_dns/system/BReactor_glib.h b/external/badvpn_dns/system/BReactor_glib.h new file mode 100644 index 00000000..0102be9d --- /dev/null +++ b/external/badvpn_dns/system/BReactor_glib.h @@ -0,0 +1,148 @@ +/** + * @file BReactor_glib.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SYSTEM_BREACTOR_H +#define BADVPN_SYSTEM_BREACTOR_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +typedef struct BReactor_s BReactor; + +struct BSmallTimer_t; + +#define BTIMER_SET_ABSOLUTE 1 +#define BTIMER_SET_RELATIVE 2 + +typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer); +typedef void (*BTimer_handler) (void *user); + +typedef struct BSmallTimer_t { + union { + BSmallTimer_handler smalll; // MSVC doesn't like "small" + BTimer_handler heavy; + } handler; + BReactor *reactor; + GSource *source; + uint8_t active; + uint8_t is_small; +} BSmallTimer; + +void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler); +int BSmallTimer_IsRunning (BSmallTimer *bt); + +typedef struct { + BSmallTimer base; + void *user; + btime_t msTime; +} BTimer; + +void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); +int BTimer_IsRunning (BTimer *bt); + +struct BFileDescriptor_t; + +#define BREACTOR_READ (1 << 0) +#define BREACTOR_WRITE (1 << 1) +#define BREACTOR_ERROR (1 << 2) +#define BREACTOR_HUP (1 << 3) + +typedef void (*BFileDescriptor_handler) (void *user, int events); + +typedef struct BFileDescriptor_t { + int fd; + BFileDescriptor_handler handler; + void *user; + int active; + int waitEvents; + BReactor *reactor; + GSource *source; + GPollFD pollfd; +} BFileDescriptor; + +void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); + +struct BReactor_s { + int exiting; + int exit_code; + GMainLoop *gloop; + int unref_gloop_on_free; + GSourceFuncs fd_source_funcs; + BPendingGroup pending_jobs; + LinkedList1 active_limits_list; + + DebugCounter d_fds_counter; + DebugCounter d_limits_ctr; + DebugCounter d_timers_ctr; + DebugObject d_obj; +}; + +int BReactor_Init (BReactor *bsys) WARN_UNUSED; +void BReactor_Free (BReactor *bsys); +int BReactor_Exec (BReactor *bsys); +void BReactor_Quit (BReactor *bsys, int code); +void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time); +void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt); +void BReactor_SetTimer (BReactor *bsys, BTimer *bt); +void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); +void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); +void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); +BPendingGroup * BReactor_PendingGroup (BReactor *bsys); +int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref); +int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; +void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); +void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); + +int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free); +GMainLoop * BReactor_GetGMainLoop (BReactor *bsys); +int BReactor_SynchronizeAll (BReactor *bsys); + +typedef struct { + BReactor *reactor; + int limit; + int count; + LinkedList1Node active_limits_list_node; + DebugObject d_obj; +} BReactorLimit; + +void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); +void BReactorLimit_Free (BReactorLimit *o); +int BReactorLimit_Increment (BReactorLimit *o); +void BReactorLimit_SetLimit (BReactorLimit *o, int limit); + +#endif diff --git a/external/badvpn_dns/system/BSignal.c b/external/badvpn_dns/system/BSignal.c new file mode 100644 index 00000000..520a167e --- /dev/null +++ b/external/badvpn_dns/system/BSignal.c @@ -0,0 +1,188 @@ +/** + * @file BSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef BADVPN_USE_WINAPI +#include +#else +#include +#include +#endif + +#include +#include + +#include + +#include + +static struct { + int initialized; + int finished; + BReactor *reactor; + BSignal_handler handler; + void *user; + #ifdef BADVPN_USE_WINAPI + BReactorIOCPOverlapped olap; + CRITICAL_SECTION iocp_handle_mutex; + HANDLE iocp_handle; + #else + BUnixSignal signal; + #endif +} bsignal_global = { + 0 +}; + +#ifdef BADVPN_USE_WINAPI + +static void olap_handler (void *user, int event, DWORD bytes) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!(event == BREACTOR_IOCP_EVENT_EXITING) || bsignal_global.finished) + + if (event == BREACTOR_IOCP_EVENT_EXITING) { + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + return; + } + + if (!bsignal_global.finished) { + // call handler + bsignal_global.handler(bsignal_global.user); + return; + } +} + +static BOOL WINAPI ctrl_handler (DWORD type) +{ + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + + if (bsignal_global.iocp_handle) { + PostQueuedCompletionStatus(bsignal_global.iocp_handle, 0, 0, &bsignal_global.olap.olap); + } + + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + return TRUE; +} + +#else + +static void unix_signal_handler (void *user, int signo) +{ + ASSERT(signo == SIGTERM || signo == SIGINT) + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + BLog(BLOG_DEBUG, "Dispatching signal"); + + // call handler + bsignal_global.handler(bsignal_global.user); + return; +} + +#endif + +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) +{ + ASSERT(!bsignal_global.initialized) + + // init arguments + bsignal_global.reactor = reactor; + bsignal_global.handler = handler; + bsignal_global.user = user; + + BLog(BLOG_DEBUG, "BSignal initializing"); + + #ifdef BADVPN_USE_WINAPI + + // init olap + BReactorIOCPOverlapped_Init(&bsignal_global.olap, bsignal_global.reactor, NULL, olap_handler); + + // init handler mutex + InitializeCriticalSection(&bsignal_global.iocp_handle_mutex); + + // remember IOCP handle + bsignal_global.iocp_handle = BReactor_GetIOCPHandle(bsignal_global.reactor); + + // configure ctrl handler + if (!SetConsoleCtrlHandler(ctrl_handler, TRUE)) { + BLog(BLOG_ERROR, "SetConsoleCtrlHandler failed"); + goto fail1; + } + + #else + + sigset_t sset; + ASSERT_FORCE(sigemptyset(&sset) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGTERM) == 0) + ASSERT_FORCE(sigaddset(&sset, SIGINT) == 0) + + // init BUnixSignal + if (!BUnixSignal_Init(&bsignal_global.signal, bsignal_global.reactor, sset, unix_signal_handler, NULL)) { + BLog(BLOG_ERROR, "BUnixSignal_Init failed"); + goto fail0; + } + + #endif + + bsignal_global.initialized = 1; + bsignal_global.finished = 0; + + return 1; + + #ifdef BADVPN_USE_WINAPI +fail1: + DeleteCriticalSection(&bsignal_global.iocp_handle_mutex); + BReactorIOCPOverlapped_Free(&bsignal_global.olap); + #endif + +fail0: + return 0; +} + +void BSignal_Finish (void) +{ + ASSERT(bsignal_global.initialized) + ASSERT(!bsignal_global.finished) + + #ifdef BADVPN_USE_WINAPI + + // forget IOCP handle + EnterCriticalSection(&bsignal_global.iocp_handle_mutex); + bsignal_global.iocp_handle = NULL; + LeaveCriticalSection(&bsignal_global.iocp_handle_mutex); + + #else + + // free BUnixSignal + BUnixSignal_Free(&bsignal_global.signal, 0); + + #endif + + bsignal_global.finished = 1; +} diff --git a/external/badvpn_dns/system/BSignal.h b/external/badvpn_dns/system/BSignal.h new file mode 100644 index 00000000..41f17e25 --- /dev/null +++ b/external/badvpn_dns/system/BSignal.h @@ -0,0 +1,64 @@ +/** + * @file BSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * A global object for catching program termination requests. + */ + +#ifndef BADVPN_SYSTEM_BSIGNAL_H +#define BADVPN_SYSTEM_BSIGNAL_H + +#include +#include + +typedef void (*BSignal_handler) (void *user); + +/** + * Initializes signal handling. + * The object is created in not capturing state. + * {@link BLog_Init} must have been done. + * + * WARNING: make sure this won't interfere with other components: + * - on Linux, this uses {@link BUnixSignal} to catch SIGTERM and SIGINT, + * - on Windows, this sets up a handler with SetConsoleCtrlHandler. + * + * @param reactor {@link BReactor} from which the handler will be called + * @param handler callback function invoked from the reactor + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BSignal_Init (BReactor *reactor, BSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Finishes signal handling. + * {@link BSignal_Init} must not be called again. + */ +void BSignal_Finish (void); + +#endif diff --git a/external/badvpn_dns/system/BThreadSignal.c b/external/badvpn_dns/system/BThreadSignal.c new file mode 100644 index 00000000..7d68c1b3 --- /dev/null +++ b/external/badvpn_dns/system/BThreadSignal.c @@ -0,0 +1,136 @@ +/** + * @file BThreadSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "BThreadSignal.h" + +#include + +static void bfd_handler (void *user, int events) +{ + BThreadSignal *o = user; + DebugObject_Access(&o->d_obj); + + char byte; + ssize_t res = read(o->pipe[0], &byte, sizeof(byte)); + + if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + return; + } + + if (res < 0) { + BLog(BLOG_ERROR, "read failed"); + return; + } + + if (res != sizeof(byte)) { + BLog(BLOG_ERROR, "bad read"); + return; + } + + o->handler(o); +} + +int BThreadSignal_Init (BThreadSignal *o, BReactor *reactor, BThreadSignal_handler handler) +{ + o->reactor = reactor; + o->handler = handler; + + if (pipe(o->pipe) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail0; + } + + if (!badvpn_set_nonblocking(o->pipe[0]) || !badvpn_set_nonblocking(o->pipe[1])) { + BLog(BLOG_ERROR, "badvpn_set_nonblocking failed"); + goto fail1; + } + + BFileDescriptor_Init(&o->bfd, o->pipe[0], bfd_handler, o); + + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + DebugObject_Init(&o->d_obj); + return 1; + +fail1: + if (close(o->pipe[1]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + if (close(o->pipe[0]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +fail0: + return 0; +} + +void BThreadSignal_Free (BThreadSignal *o) +{ + DebugObject_Free(&o->d_obj); + + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + if (close(o->pipe[1]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } + if (close(o->pipe[0]) < 0) { + BLog(BLOG_ERROR, "close failed"); + } +} + +int BThreadSignal_Thread_Signal (BThreadSignal *o) +{ + DebugObject_Access(&o->d_obj); + + char byte = 0; + ssize_t res = write(o->pipe[1], &byte, sizeof(byte)); + + if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + return 0; + } + + if (res < 0) { + BLog(BLOG_ERROR, "write failed"); + } else if (res != sizeof(byte)) { + BLog(BLOG_ERROR, "bad write"); + } + + return 1; +} diff --git a/external/badvpn_dns/system/BThreadSignal.h b/external/badvpn_dns/system/BThreadSignal.h new file mode 100644 index 00000000..2c8d8169 --- /dev/null +++ b/external/badvpn_dns/system/BThreadSignal.h @@ -0,0 +1,53 @@ +/** + * @file BThreadSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_B_THREAD_SIGNAL_H +#define BADVPN_B_THREAD_SIGNAL_H + +#include +#include +#include + +typedef struct BThreadSignal_s BThreadSignal; + +typedef void (*BThreadSignal_handler) (BThreadSignal *thread_signal); + +struct BThreadSignal_s { + BReactor *reactor; + BThreadSignal_handler handler; + int pipe[2]; + BFileDescriptor bfd; + DebugObject d_obj; +}; + +int BThreadSignal_Init (BThreadSignal *o, BReactor *reactor, BThreadSignal_handler handler) WARN_UNUSED; +void BThreadSignal_Free (BThreadSignal *o); +int BThreadSignal_Thread_Signal (BThreadSignal *o); + +#endif diff --git a/external/badvpn_dns/system/BTime.c b/external/badvpn_dns/system/BTime.c new file mode 100644 index 00000000..a1fa9e71 --- /dev/null +++ b/external/badvpn_dns/system/BTime.c @@ -0,0 +1,38 @@ +/** + * @file BTime.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#ifndef BADVPN_PLUGIN +struct _BTime_global btime_global = { + #ifndef NDEBUG + 0 + #endif +}; +#endif diff --git a/external/badvpn_dns/system/BTime.h b/external/badvpn_dns/system/BTime.h new file mode 100644 index 00000000..6998814a --- /dev/null +++ b/external/badvpn_dns/system/BTime.h @@ -0,0 +1,163 @@ +/** + * @file BTime.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * System time abstraction used by {@link BReactor}. + */ + +#ifndef BADVPN_SYSTEM_BTIME_H +#define BADVPN_SYSTEM_BTIME_H + +#if defined(BADVPN_USE_WINAPI) +#include +#elif defined(BADVPN_EMSCRIPTEN) +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include + +#include + +typedef int64_t btime_t; + +#define BTIME_MIN INT64_MIN + +struct _BTime_global { + #ifndef NDEBUG + int initialized; // initialized statically + #endif + #if defined(BADVPN_USE_WINAPI) + LARGE_INTEGER start_time; + #elif defined(BADVPN_EMSCRIPTEN) + btime_t start_time; + #else + btime_t start_time; + int use_gettimeofday; + #endif +}; + +extern struct _BTime_global btime_global; + +static void BTime_Init (void) +{ + ASSERT(!btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + ASSERT_FORCE(QueryPerformanceCounter(&btime_global.start_time)) + + #elif defined(BADVPN_EMSCRIPTEN) + + btime_global.start_time = emscripten_get_now(); + + #else + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { + BLog(BLOG_WARNING, "CLOCK_MONOTONIC is not available. Timers will be confused by clock changes."); + + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + + btime_global.start_time = (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000; + btime_global.use_gettimeofday = 1; + } else { + btime_global.start_time = (int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000; + btime_global.use_gettimeofday = 0; + } + + #endif + + #ifndef NDEBUG + btime_global.initialized = 1; + #endif +} + +static btime_t btime_gettime (void) +{ + ASSERT(btime_global.initialized) + + #if defined(BADVPN_USE_WINAPI) + + LARGE_INTEGER count; + LARGE_INTEGER freq; + ASSERT_FORCE(QueryPerformanceCounter(&count)) + ASSERT_FORCE(QueryPerformanceFrequency(&freq)) + return (((count.QuadPart - btime_global.start_time.QuadPart) * 1000) / freq.QuadPart); + + #elif defined(BADVPN_EMSCRIPTEN) + + return (btime_t)emscripten_get_now() - btime_global.start_time; + + #else + + if (btime_global.use_gettimeofday) { + struct timeval tv; + ASSERT_FORCE(gettimeofday(&tv, NULL) == 0) + return ((int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec/1000); + } else { + struct timespec ts; + ASSERT_FORCE(clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (((int64_t)ts.tv_sec * 1000 + (int64_t)ts.tv_nsec/1000000) - btime_global.start_time); + } + + #endif +} + +static btime_t btime_add (btime_t t1, btime_t t2) +{ + // handle overflow + int overflows = add_int64_overflows(t1, t2); + btime_t sum; + if (overflows != 0) { + if (overflows > 0) { + sum = INT64_MAX; + } else { + sum = INT64_MIN; + } + } else { + sum = t1 + t2; + } + + return sum; +} + +static btime_t btime_getpast (void) +{ + return INT64_MIN; +} + +#endif diff --git a/external/badvpn_dns/system/BUnixSignal.c b/external/badvpn_dns/system/BUnixSignal.c new file mode 100644 index 00000000..94edd6b3 --- /dev/null +++ b/external/badvpn_dns/system/BUnixSignal.c @@ -0,0 +1,406 @@ +/** + * @file BUnixSignal.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef BADVPN_USE_SIGNALFD +#include +#endif + +#include +#include +#include + +#include + +#include + +#define BUNIXSIGNAL_MAX_SIGNALS 64 + +#ifdef BADVPN_USE_SIGNALFD + +static void signalfd_handler (BUnixSignal *o, int events) +{ + DebugObject_Access(&o->d_obj); + + // read a signal + struct signalfd_siginfo siginfo; + int bytes = read(o->signalfd_fd, &siginfo, sizeof(siginfo)); + if (bytes < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + ASSERT_FORCE(bytes == sizeof(siginfo)) + + // check signal + if (siginfo.ssi_signo > INT_MAX) { + BLog(BLOG_ERROR, "read returned out of int range signo (%"PRIu32")", siginfo.ssi_signo); + return; + } + int signo = siginfo.ssi_signo; + if (sigismember(&o->signals, signo) <= 0) { + BLog(BLOG_ERROR, "read returned wrong signo (%d)", signo); + return; + } + + BLog(BLOG_DEBUG, "dispatching signal %d", signo); + + // call handler + o->handler(o->user, signo); + return; +} + +#endif + +#ifdef BADVPN_USE_KEVENT + +static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags, intptr_t data) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // call signal + o->handler(o->user, entry->signo); + return; +} + +#endif + +#ifdef BADVPN_USE_SELFPIPE + +struct BUnixSignal_selfpipe_entry *bunixsignal_selfpipe_entries[BUNIXSIGNAL_MAX_SIGNALS]; + +static void free_selfpipe_entry (struct BUnixSignal_selfpipe_entry *entry) +{ + BUnixSignal *o = entry->parent; + + // uninstall signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + ASSERT_FORCE(sigaction(entry->signo, &act, NULL) == 0) + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + + // close pipe + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) +} + +static void pipe_read_fd_handler (struct BUnixSignal_selfpipe_entry *entry, int events) +{ + BUnixSignal *o = entry->parent; + DebugObject_Access(&o->d_obj); + + // read a byte + uint8_t b; + if (read(entry->pipefds[0], &b, sizeof(b)) < 0) { + int error = errno; + if (error == EAGAIN || error == EWOULDBLOCK) { + return; + } + BLog(BLOG_ERROR, "read failed (%d)", error); + return; + } + + // call handler + o->handler(o->user, entry->signo); + return; +} + +static void signal_handler (int signo) +{ + ASSERT(signo >= 0) + ASSERT(signo < BUNIXSIGNAL_MAX_SIGNALS) + + struct BUnixSignal_selfpipe_entry *entry = bunixsignal_selfpipe_entries[signo]; + + uint8_t b = 0; + write(entry->pipefds[1], &b, sizeof(b)); +} + +#endif + +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) +{ + // init arguments + o->reactor = reactor; + o->signals = signals; + o->handler = handler; + o->user = user; + + #ifdef BADVPN_USE_SIGNALFD + + // init signalfd fd + if ((o->signalfd_fd = signalfd(-1, &o->signals, 0)) < 0) { + BLog(BLOG_ERROR, "signalfd failed"); + goto fail0; + } + + // set non-blocking + if (fcntl(o->signalfd_fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init signalfd BFileDescriptor + BFileDescriptor_Init(&o->signalfd_bfd, o->signalfd_fd, (BFileDescriptor_handler)signalfd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->signalfd_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->signalfd_bfd, BREACTOR_READ); + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_KEVENT + + // count signals + int num_signals = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init kevents + o->num_entries = 0; + for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + struct BUnixSignal_kevent_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + if (!BReactorKEvent_Init(&entry->kevent, o->reactor, (BReactorKEvent_handler)kevent_handler, entry, entry->signo, EVFILT_SIGNAL, 0, 0)) { + BLog(BLOG_ERROR, "BReactorKEvent_Init failed"); + goto fail2; + } + o->num_entries++; + } + + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + goto fail2; + } + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + // count signals + int num_signals = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + num_signals++; + } + + // allocate array + if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail0; + } + + // init entries + o->num_entries = 0; + for (int i = 1; i < BUNIXSIGNAL_MAX_SIGNALS; i++) { + if (!sigismember(&o->signals, i)) { + continue; + } + + struct BUnixSignal_selfpipe_entry *entry = &o->entries[o->num_entries]; + entry->parent = o; + entry->signo = i; + + // init pipe + if (pipe(entry->pipefds) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto loop_fail0; + } + + // set pipe ends non-blocking + if (!badvpn_set_nonblocking(entry->pipefds[0]) || !badvpn_set_nonblocking(entry->pipefds[1])) { + BLog(BLOG_ERROR, "set nonblocking failed"); + goto loop_fail1; + } + + // init read end BFileDescriptor + BFileDescriptor_Init(&entry->pipe_read_bfd, entry->pipefds[0], (BFileDescriptor_handler)pipe_read_fd_handler, entry); + if (!BReactor_AddFileDescriptor(o->reactor, &entry->pipe_read_bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto loop_fail1; + } + BReactor_SetFileDescriptorEvents(o->reactor, &entry->pipe_read_bfd, BREACTOR_READ); + + // set global entry pointer + bunixsignal_selfpipe_entries[entry->signo] = entry; + + // install signal handler + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + sigemptyset(&act.sa_mask); + if (sigaction(entry->signo, &act, NULL) < 0) { + BLog(BLOG_ERROR, "sigaction failed"); + goto loop_fail2; + } + + o->num_entries++; + + continue; + + loop_fail2: + BReactor_RemoveFileDescriptor(o->reactor, &entry->pipe_read_bfd); + loop_fail1: + ASSERT_FORCE(close(entry->pipefds[0]) == 0) + ASSERT_FORCE(close(entry->pipefds[1]) == 0) + loop_fail0: + goto fail2; + } + + #endif + + DebugObject_Init(&o->d_obj); + + return 1; + + #ifdef BADVPN_USE_SIGNALFD +fail2: + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); +fail1: + ASSERT_FORCE(close(o->signalfd_fd) == 0) + #endif + + #ifdef BADVPN_USE_KEVENT +fail2: + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + BFree(o->entries); + #endif + + #ifdef BADVPN_USE_SELFPIPE +fail2: + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + BFree(o->entries); + #endif + +fail0: + return 0; +} + +void BUnixSignal_Free (BUnixSignal *o, int unblock) +{ + ASSERT(unblock == 0 || unblock == 1) + DebugObject_Free(&o->d_obj); + + #ifdef BADVPN_USE_SIGNALFD + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free signalfd BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd); + + // free signalfd fd + ASSERT_FORCE(close(o->signalfd_fd) == 0) + + #endif + + #ifdef BADVPN_USE_KEVENT + + if (unblock) { + // unblock signals + ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0) + } + + // free kevents + while (o->num_entries > 0) { + BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif + + #ifdef BADVPN_USE_SELFPIPE + + if (!unblock) { + // block signals + if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) { + BLog(BLOG_ERROR, "sigprocmask block failed"); + } + } + + // free entries + while (o->num_entries > 0) { + free_selfpipe_entry(&o->entries[o->num_entries - 1]); + o->num_entries--; + } + + // free array + BFree(o->entries); + + #endif +} diff --git a/external/badvpn_dns/system/BUnixSignal.h b/external/badvpn_dns/system/BUnixSignal.h new file mode 100644 index 00000000..2438be9b --- /dev/null +++ b/external/badvpn_dns/system/BUnixSignal.h @@ -0,0 +1,132 @@ +/** + * @file BUnixSignal.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * Object for catching unix signals. + */ + +#ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H +#define BADVPN_SYSTEM_BUNIXSIGNAL_H + +#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_SELFPIPE)) != 1 +#error Unknown signal backend or too many signal backends +#endif + +#include +#include + +#include +#include +#include + +struct BUnixSignal_s; + +/** + * Handler function called when a signal is received. + * + * @param user as in {@link BUnixSignal_Init} + * @param signo signal number. Will be one of the signals provided to {@link signals}. + */ +typedef void (*BUnixSignal_handler) (void *user, int signo); + +#ifdef BADVPN_USE_KEVENT +struct BUnixSignal_kevent_entry { + struct BUnixSignal_s *parent; + int signo; + BReactorKEvent kevent; +}; +#endif + +#ifdef BADVPN_USE_SELFPIPE +struct BUnixSignal_selfpipe_entry { + struct BUnixSignal_s *parent; + int signo; + int pipefds[2]; + BFileDescriptor pipe_read_bfd; +}; +#endif + +/** + * Object for catching unix signals. + */ +typedef struct BUnixSignal_s { + BReactor *reactor; + sigset_t signals; + BUnixSignal_handler handler; + void *user; + + #ifdef BADVPN_USE_SIGNALFD + int signalfd_fd; + BFileDescriptor signalfd_bfd; + #endif + + #ifdef BADVPN_USE_KEVENT + struct BUnixSignal_kevent_entry *entries; + int num_entries; + #endif + + #ifdef BADVPN_USE_SELFPIPE + struct BUnixSignal_selfpipe_entry *entries; + int num_entries; + #endif + + DebugObject d_obj; +} BUnixSignal; + +/** + * Initializes the object. + * {@link BLog_Init} must have been done. + * + * WARNING: for every signal number there should be at most one {@link BUnixSignal} + * object handling it (or anything else that could interfere). + * + * This blocks the signal using sigprocmask() and sets up signalfd() for receiving + * signals. + * + * @param o the object + * @param reactor reactor we live in + * @param signals signals to handle. See man 3 sigsetops. + * @param handler handler function to call when a signal is received + * @param user value passed to callback function + * @return 1 on success, 0 on failure + */ +int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user) WARN_UNUSED; + +/** + * Frees the object. + * + * @param o the object + * @param unblock whether to unblock the signals using sigprocmask(). Not unblocking it + * can be used while the program is exiting gracefully to prevent the + * signals from being handled handled according to its default disposition + * after this function is called. Must be 0 or 1. + */ +void BUnixSignal_Free (BUnixSignal *o, int unblock); + +#endif diff --git a/external/badvpn_dns/system/CMakeLists.txt b/external/badvpn_dns/system/CMakeLists.txt new file mode 100644 index 00000000..99f7333a --- /dev/null +++ b/external/badvpn_dns/system/CMakeLists.txt @@ -0,0 +1,44 @@ +set(BSYSTEM_ADDITIONAL_LIBS) +set(BSYSTEM_ADDITIONAL_SOURCES) + +if (NOT EMSCRIPTEN) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BSignal.c + BNetwork.c + ) + + if (WIN32) + list(APPEND BSYSTEM_ADDITIONAL_LIBS ws2_32 mswsock) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BConnection_win.c + BDatagram_win.c + ) + endif () + + if (NOT WIN32) + list(APPEND BSYSTEM_ADDITIONAL_SOURCES + BUnixSignal.c + BConnection_unix.c + BDatagram_unix.c + BProcess.c + BInputProcess.c + BThreadSignal.c + BLockReactor.c + ) + endif () +endif () + +if (BREACTOR_BACKEND STREQUAL "badvpn") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_badvpn.c) +elseif (BREACTOR_BACKEND STREQUAL "glib") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_glib.c) + list(APPEND BSYSTEM_ADDITIONAL_LIBS ${GLIB2_LIBRARIES}) +elseif (BREACTOR_BACKEND STREQUAL "emscripten") + list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_emscripten.c) +endif () + +set(SYSTEM_SOURCES + BTime.c + ${BSYSTEM_ADDITIONAL_SOURCES} +) +badvpn_add_library(system "base;flow" "${BSYSTEM_ADDITIONAL_LIBS}" "${SYSTEM_SOURCES}") diff --git a/external/badvpn_dns/tests/CMakeLists.txt b/external/badvpn_dns/tests/CMakeLists.txt new file mode 100644 index 00000000..ebb0d62b --- /dev/null +++ b/external/badvpn_dns/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(chunkbuffer2_test chunkbuffer2_test.c) + +add_executable(bproto_test bproto_test.c) + +if (BUILDING_THREADWORK) + add_executable(threadwork_test threadwork_test.c) + target_link_libraries(threadwork_test threadwork) +endif () diff --git a/external/badvpn_dns/tests/bproto_test.bproto b/external/badvpn_dns/tests/bproto_test.bproto new file mode 100644 index 00000000..4641a747 --- /dev/null +++ b/external/badvpn_dns/tests/bproto_test.bproto @@ -0,0 +1,9 @@ +message msg1 { + required uint16 a = 5; + optional uint32 b = 6; + required repeated uint64 c = 7; + repeated uint16 d = 8; + required uint8 e = 9; + required data f = 10; + required data("4") g = 11; +}; diff --git a/external/badvpn_dns/tests/bproto_test.c b/external/badvpn_dns/tests/bproto_test.c new file mode 100644 index 00000000..067695b0 --- /dev/null +++ b/external/badvpn_dns/tests/bproto_test.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include +#include + +#include + +int main () +{ + uint16_t a = 17501; + uint64_t c = 82688926; + uint16_t d1 = 1517; + uint16_t d2 = 1518; + uint8_t e = 72; + const char *f = "hello world"; + const char *g = "helo"; + + // encode message + + int len = msg1_SIZEa + msg1_SIZEc + msg1_SIZEd + msg1_SIZEd + msg1_SIZEe + msg1_SIZEf(strlen(f)) + msg1_SIZEg; + + uint8_t *msg = (uint8_t *)BAlloc(len); + ASSERT_FORCE(msg) + msg1Writer writer; + msg1Writer_Init(&writer, msg); + msg1Writer_Adda(&writer, a); + msg1Writer_Addc(&writer, c); + msg1Writer_Addd(&writer, d1); + msg1Writer_Addd(&writer, d2); + msg1Writer_Adde(&writer, e); + uint8_t *f_dst = msg1Writer_Addf(&writer, strlen(f)); + memcpy(f_dst, f, strlen(f)); + uint8_t *g_dst = msg1Writer_Addg(&writer); + memcpy(g_dst, g, strlen(g)); + int len2 = msg1Writer_Finish(&writer); + ASSERT_EXECUTE(len2 == len) + + // parse message + + msg1Parser parser; + ASSERT_EXECUTE(msg1Parser_Init(&parser, msg, len)) + + // check parse results + + uint16_t p_a; + uint64_t p_c; + uint16_t p_d1; + uint16_t p_d2; + uint8_t p_e; + uint8_t *p_f; + int p_f_len; + uint8_t *p_g; + ASSERT_EXECUTE(msg1Parser_Geta(&parser, &p_a)) + ASSERT_EXECUTE(msg1Parser_Getc(&parser, &p_c)) + ASSERT_EXECUTE(msg1Parser_Getd(&parser, &p_d1)) + ASSERT_EXECUTE(msg1Parser_Getd(&parser, &p_d2)) + ASSERT_EXECUTE(msg1Parser_Gete(&parser, &p_e)) + ASSERT_EXECUTE(msg1Parser_Getf(&parser, &p_f, &p_f_len)) + ASSERT_EXECUTE(msg1Parser_Getg(&parser, &p_g)) + + ASSERT(p_a == a) + ASSERT(p_c == c) + ASSERT(p_d1 == d1) + ASSERT(p_d2 == d2) + ASSERT(p_e == e) + ASSERT(p_f_len == strlen(f) && !memcmp(p_f, f, p_f_len)) + ASSERT(!memcmp(p_g, g, strlen(g))) + + ASSERT(msg1Parser_GotEverything(&parser)) + + BFree(msg); + + return 0; +} diff --git a/external/badvpn_dns/tests/chunkbuffer2_test.c b/external/badvpn_dns/tests/chunkbuffer2_test.c new file mode 100644 index 00000000..ff94bb35 --- /dev/null +++ b/external/badvpn_dns/tests/chunkbuffer2_test.c @@ -0,0 +1,86 @@ +#include +#include + +int main () +{ + struct ChunkBuffer2_block blocks[16]; + ChunkBuffer2 buf; + ChunkBuffer2_Init(&buf, blocks, 16, 4 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 15 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == NULL) + ASSERT_FORCE(buf.output_avail == -1) + + ChunkBuffer2_SubmitPacket(&buf, sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[3]) + ASSERT_FORCE(buf.input_avail == 13 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 8 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.input_avail == 4 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 4 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == NULL) + ASSERT_FORCE(buf.input_avail == -1) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 1 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[3]) + ASSERT_FORCE(buf.output_avail == 8 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 10 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.output_avail == 4 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 9 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.input_avail == 0 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[12]) + ASSERT_FORCE(buf.output_avail == 4 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.input_avail == 5 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 9 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_SubmitPacket(&buf, 1 * sizeof(struct ChunkBuffer2_block)); + + ASSERT_FORCE(buf.input_dest == NULL) + ASSERT_FORCE(buf.input_avail == -1) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.output_avail == 9 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 9 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == (uint8_t *)&blocks[11]) + ASSERT_FORCE(buf.output_avail == 1 * sizeof(struct ChunkBuffer2_block)) + + ChunkBuffer2_ConsumePacket(&buf); + + ASSERT_FORCE(buf.input_dest == (uint8_t *)&blocks[1]) + ASSERT_FORCE(buf.input_avail == 15 * sizeof(struct ChunkBuffer2_block)) + ASSERT_FORCE(buf.output_dest == NULL) + ASSERT_FORCE(buf.output_avail == -1) + + return 0; +} diff --git a/external/badvpn_dns/tests/threadwork_test.c b/external/badvpn_dns/tests/threadwork_test.c new file mode 100644 index 00000000..9c18b4b9 --- /dev/null +++ b/external/badvpn_dns/tests/threadwork_test.c @@ -0,0 +1,87 @@ +#include +#include + +#include +#include +#include +#include + +BReactor reactor; +BThreadWorkDispatcher twd; +BThreadWork tw1; +BThreadWork tw2; +BThreadWork tw3; +int num_left; + +static void handler_done (void *user) +{ + printf("work done\n"); + + num_left--; + + if (num_left == 0) { + printf("all works done, quitting\n"); + BReactor_Quit(&reactor, 0); + } +} + +static void work_func (void *user) +{ + unsigned int x = 0; + + for (int i = 0; i < 10000; i++) { + for (int j = 0; j < 10000; j++) { + x++; + } + } +} + +static void dummy_works (int n) +{ + for (int i = 0; i < n; i++) { + BThreadWork tw_tmp; + BThreadWork_Init(&tw_tmp, &twd, handler_done, NULL, work_func, NULL); + BThreadWork_Free(&tw_tmp); + } +} + +int main () +{ + BLog_InitStdout(); + BLog_SetChannelLoglevel(BLOG_CHANNEL_BThreadWork, BLOG_DEBUG); + + if (!BReactor_Init(&reactor)) { + DEBUG("BReactor_Init failed"); + goto fail1; + } + + if (!BThreadWorkDispatcher_Init(&twd, &reactor, 1)) { + DEBUG("BThreadWorkDispatcher_Init failed"); + goto fail2; + } + + dummy_works(200); + + BThreadWork_Init(&tw1, &twd, handler_done, NULL, work_func, NULL); + + BThreadWork_Init(&tw2, &twd, handler_done, NULL, work_func, NULL); + + BThreadWork_Init(&tw3, &twd, handler_done, NULL, work_func, NULL); + + dummy_works(200); + + num_left = 3; + + BReactor_Exec(&reactor); + + BThreadWork_Free(&tw3); + BThreadWork_Free(&tw2); + BThreadWork_Free(&tw1); + BThreadWorkDispatcher_Free(&twd); +fail2: + BReactor_Free(&reactor); +fail1: + BLog_Free(); + DebugObjectGlobal_Finish(); + return 0; +} diff --git a/external/badvpn_dns/threadwork/BThreadWork.c b/external/badvpn_dns/threadwork/BThreadWork.c new file mode 100644 index 00000000..3c29f952 --- /dev/null +++ b/external/badvpn_dns/threadwork/BThreadWork.c @@ -0,0 +1,451 @@ +/** + * @file BThreadWork.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include + #include + #include +#endif + +#include +#include + +#include + +#include + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + +static void * dispatcher_thread (struct BThreadWorkDispatcher_thread *t) +{ + BThreadWorkDispatcher *o = t->d; + + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + while (1) { + // exit if requested + if (o->cancel) { + break; + } + + if (LinkedList1_IsEmpty(&o->pending_list)) { + // wait for event + ASSERT_FORCE(pthread_cond_wait(&t->new_cond, &o->mutex) == 0) + continue; + } + + // grab the work + BThreadWork *w = UPPER_OBJECT(LinkedList1_GetFirst(&o->pending_list), BThreadWork, list_node); + ASSERT(w->state == BTHREADWORK_STATE_PENDING) + LinkedList1_Remove(&o->pending_list, &w->list_node); + t->running_work = w; + w->state = BTHREADWORK_STATE_RUNNING; + + // do the work + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + w->work_func(w->work_func_user); + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + // release the work + t->running_work = NULL; + LinkedList1_Append(&o->finished_list, &w->list_node); + w->state = BTHREADWORK_STATE_FINISHED; + ASSERT_FORCE(sem_post(&w->finished_sem) == 0) + + // write to pipe + uint8_t b = 0; + int res = write(o->pipe[1], &b, sizeof(b)); + if (res < 0) { + int error = errno; + ASSERT_FORCE(error == EAGAIN || error == EWOULDBLOCK) + } + } + + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + return NULL; +} + +static void dispatch_job (BThreadWorkDispatcher *o) +{ + ASSERT(o->num_threads > 0) + + // lock + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + + // check for finished job + if (LinkedList1_IsEmpty(&o->finished_list)) { + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + return; + } + + // grab finished job + BThreadWork *w = UPPER_OBJECT(LinkedList1_GetFirst(&o->finished_list), BThreadWork, list_node); + ASSERT(w->state == BTHREADWORK_STATE_FINISHED) + LinkedList1_Remove(&o->finished_list, &w->list_node); + + // schedule more + if (!LinkedList1_IsEmpty(&o->finished_list)) { + BPending_Set(&o->more_job); + } + + // set state forgotten + w->state = BTHREADWORK_STATE_FORGOTTEN; + + // unlock + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + // call handler + w->handler_done(w->user); + return; +} + +static void pipe_fd_handler (BThreadWorkDispatcher *o, int events) +{ + ASSERT(o->num_threads > 0) + DebugObject_Access(&o->d_obj); + + // read data from pipe + uint8_t b[64]; + int res = read(o->pipe[0], b, sizeof(b)); + if (res < 0) { + int error = errno; + ASSERT_FORCE(error == EAGAIN || error == EWOULDBLOCK) + } else { + ASSERT(res > 0) + } + + dispatch_job(o); + return; +} + +static void more_job_handler (BThreadWorkDispatcher *o) +{ + ASSERT(o->num_threads > 0) + DebugObject_Access(&o->d_obj); + + dispatch_job(o); + return; +} + +static void stop_threads (BThreadWorkDispatcher *o) +{ + // set cancelling + ASSERT_FORCE(pthread_mutex_lock(&o->mutex) == 0) + o->cancel = 1; + ASSERT_FORCE(pthread_mutex_unlock(&o->mutex) == 0) + + while (o->num_threads > 0) { + struct BThreadWorkDispatcher_thread *t = &o->threads[o->num_threads - 1]; + + // wake up thread + ASSERT_FORCE(pthread_cond_signal(&t->new_cond) == 0) + + // wait for thread to exit + ASSERT_FORCE(pthread_join(t->thread, NULL) == 0) + + // free condition variable + ASSERT_FORCE(pthread_cond_destroy(&t->new_cond) == 0) + + o->num_threads--; + } +} + +#endif + +static void work_job_handler (BThreadWork *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + ASSERT(o->d->num_threads == 0) + #endif + DebugObject_Access(&o->d_obj); + + // do the work + o->work_func(o->work_func_user); + + // call handler + o->handler_done(o->user); + return; +} + +int BThreadWorkDispatcher_Init (BThreadWorkDispatcher *o, BReactor *reactor, int num_threads_hint) +{ + // init arguments + o->reactor = reactor; + + if (num_threads_hint < 0) { + num_threads_hint = 2; + } + if (num_threads_hint > BTHREADWORK_MAX_THREADS) { + num_threads_hint = BTHREADWORK_MAX_THREADS; + } + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + if (num_threads_hint > 0) { + // init pending list + LinkedList1_Init(&o->pending_list); + + // init finished list + LinkedList1_Init(&o->finished_list); + + // init mutex + if (pthread_mutex_init(&o->mutex, NULL) != 0) { + BLog(BLOG_ERROR, "pthread_mutex_init failed"); + goto fail0; + } + + // init pipe + if (pipe(o->pipe) < 0) { + BLog(BLOG_ERROR, "pipe failed"); + goto fail1; + } + + // set read end non-blocking + if (fcntl(o->pipe[0], F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "fcntl failed"); + goto fail2; + } + + // set write end non-blocking + if (fcntl(o->pipe[1], F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "fcntl failed"); + goto fail2; + } + + // init BFileDescriptor + BFileDescriptor_Init(&o->bfd, o->pipe[0], (BFileDescriptor_handler)pipe_fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail2; + } + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ); + + // init more job + BPending_Init(&o->more_job, BReactor_PendingGroup(o->reactor), (BPending_handler)more_job_handler, o); + + // set not cancelling + o->cancel = 0; + + // init threads + o->num_threads = 0; + for (int i = 0; i < num_threads_hint; i++) { + struct BThreadWorkDispatcher_thread *t = &o->threads[i]; + + // set parent pointer + t->d = o; + + // set no running work + t->running_work = NULL; + + // init condition variable + if (pthread_cond_init(&t->new_cond, NULL) != 0) { + BLog(BLOG_ERROR, "pthread_cond_init failed"); + goto fail3; + } + + // init thread + if (pthread_create(&t->thread, NULL, (void * (*) (void *))dispatcher_thread, t) != 0) { + BLog(BLOG_ERROR, "pthread_create failed"); + ASSERT_FORCE(pthread_cond_destroy(&t->new_cond) == 0) + goto fail3; + } + + o->num_threads++; + } + } + + #endif + + DebugObject_Init(&o->d_obj); + DebugCounter_Init(&o->d_ctr); + return 1; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD +fail3: + stop_threads(o); + BPending_Free(&o->more_job); + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); +fail2: + ASSERT_FORCE(close(o->pipe[0]) == 0) + ASSERT_FORCE(close(o->pipe[1]) == 0) +fail1: + ASSERT_FORCE(pthread_mutex_destroy(&o->mutex) == 0) +fail0: + return 0; + #endif +} + +void BThreadWorkDispatcher_Free (BThreadWorkDispatcher *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (o->num_threads > 0) { + ASSERT(LinkedList1_IsEmpty(&o->pending_list)) + for (int i = 0; i < o->num_threads; i++) { ASSERT(!o->threads[i].running_work) } + ASSERT(LinkedList1_IsEmpty(&o->finished_list)) + } + #endif + DebugObject_Free(&o->d_obj); + DebugCounter_Free(&o->d_ctr); + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + + if (o->num_threads > 0) { + // stop threads + stop_threads(o); + + // free more job + BPending_Free(&o->more_job); + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + // free pipe + ASSERT_FORCE(close(o->pipe[0]) == 0) + ASSERT_FORCE(close(o->pipe[1]) == 0) + + // free mutex + ASSERT_FORCE(pthread_mutex_destroy(&o->mutex) == 0) + } + + #endif +} + +int BThreadWorkDispatcher_UsingThreads (BThreadWorkDispatcher *o) +{ + #ifdef BADVPN_THREADWORK_USE_PTHREAD + return (o->num_threads > 0); + #else + return 0; + #endif +} + +void BThreadWork_Init (BThreadWork *o, BThreadWorkDispatcher *d, BThreadWork_handler_done handler_done, void *user, BThreadWork_work_func work_func, void *work_func_user) +{ + DebugObject_Access(&d->d_obj); + + // init arguments + o->d = d; + o->handler_done = handler_done; + o->user = user; + o->work_func = work_func; + o->work_func_user = work_func_user; + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (d->num_threads > 0) { + // set state + o->state = BTHREADWORK_STATE_PENDING; + + // init finished semaphore + ASSERT_FORCE(sem_init(&o->finished_sem, 0, 0) == 0) + + // post work + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + LinkedList1_Append(&d->pending_list, &o->list_node); + for (int i = 0; i < d->num_threads; i++) { + if (!d->threads[i].running_work) { + ASSERT_FORCE(pthread_cond_signal(&d->threads[i].new_cond) == 0) + break; + } + } + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + } else { + #endif + // schedule job + BPending_Init(&o->job, BReactor_PendingGroup(d->reactor), (BPending_handler)work_job_handler, o); + BPending_Set(&o->job); + #ifdef BADVPN_THREADWORK_USE_PTHREAD + } + #endif + + DebugObject_Init(&o->d_obj); + DebugCounter_Increment(&d->d_ctr); +} + +void BThreadWork_Free (BThreadWork *o) +{ + BThreadWorkDispatcher *d = o->d; + DebugObject_Free(&o->d_obj); + DebugCounter_Decrement(&d->d_ctr); + + #ifdef BADVPN_THREADWORK_USE_PTHREAD + if (d->num_threads > 0) { + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + + switch (o->state) { + case BTHREADWORK_STATE_PENDING: { + BLog(BLOG_DEBUG, "remove pending work"); + + // remove from pending list + LinkedList1_Remove(&d->pending_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_RUNNING: { + BLog(BLOG_DEBUG, "remove running work"); + + // wait for the work to finish running + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + ASSERT_FORCE(sem_wait(&o->finished_sem) == 0) + ASSERT_FORCE(pthread_mutex_lock(&d->mutex) == 0) + + ASSERT(o->state == BTHREADWORK_STATE_FINISHED) + + // remove from finished list + LinkedList1_Remove(&d->finished_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_FINISHED: { + BLog(BLOG_DEBUG, "remove finished work"); + + // remove from finished list + LinkedList1_Remove(&d->finished_list, &o->list_node); + } break; + + case BTHREADWORK_STATE_FORGOTTEN: { + BLog(BLOG_DEBUG, "remove forgotten work"); + } break; + + default: + ASSERT(0); + } + + ASSERT_FORCE(pthread_mutex_unlock(&d->mutex) == 0) + + // free finished semaphore + ASSERT_FORCE(sem_destroy(&o->finished_sem) == 0) + } else { + #endif + BPending_Free(&o->job); + #ifdef BADVPN_THREADWORK_USE_PTHREAD + } + #endif +} diff --git a/external/badvpn_dns/threadwork/BThreadWork.h b/external/badvpn_dns/threadwork/BThreadWork.h new file mode 100644 index 00000000..e29c0805 --- /dev/null +++ b/external/badvpn_dns/threadwork/BThreadWork.h @@ -0,0 +1,171 @@ +/** + * @file BThreadWork.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * System for performing computations (possibly) in parallel with the event loop + * in a different thread. + */ + +#ifndef BADVPN_BTHREADWORK_BTHREADWORK_H +#define BADVPN_BTHREADWORK_BTHREADWORK_H + +#ifdef BADVPN_THREADWORK_USE_PTHREAD + #include + #include +#endif + +#include +#include +#include +#include + +#define BTHREADWORK_STATE_PENDING 1 +#define BTHREADWORK_STATE_RUNNING 2 +#define BTHREADWORK_STATE_FINISHED 3 +#define BTHREADWORK_STATE_FORGOTTEN 4 + +#define BTHREADWORK_MAX_THREADS 8 + +struct BThreadWork_s; +struct BThreadWorkDispatcher_s; + +/** + * Function called to do the work for a {@link BThreadWork}. + * The function may be called in another thread, in parallel with the event loop. + * + * @param user as work_func_user in {@link BThreadWork_Init} + */ +typedef void (*BThreadWork_work_func) (void *user); + +/** + * Handler called when a {@link BThreadWork} work is done. + * + * @param user as in {@link BThreadWork_Init} + */ +typedef void (*BThreadWork_handler_done) (void *user); + +#ifdef BADVPN_THREADWORK_USE_PTHREAD +struct BThreadWorkDispatcher_thread { + struct BThreadWorkDispatcher_s *d; + struct BThreadWork_s *running_work; + pthread_cond_t new_cond; + pthread_t thread; +}; +#endif + +typedef struct BThreadWorkDispatcher_s { + BReactor *reactor; + #ifdef BADVPN_THREADWORK_USE_PTHREAD + LinkedList1 pending_list; + LinkedList1 finished_list; + pthread_mutex_t mutex; + int pipe[2]; + BFileDescriptor bfd; + BPending more_job; + int cancel; + int num_threads; + struct BThreadWorkDispatcher_thread threads[BTHREADWORK_MAX_THREADS]; + #endif + DebugObject d_obj; + DebugCounter d_ctr; +} BThreadWorkDispatcher; + +typedef struct BThreadWork_s { + BThreadWorkDispatcher *d; + BThreadWork_handler_done handler_done; + void *user; + BThreadWork_work_func work_func; + void *work_func_user; + union { + #ifdef BADVPN_THREADWORK_USE_PTHREAD + struct { + LinkedList1Node list_node; + int state; + sem_t finished_sem; + }; + #endif + struct { + BPending job; + }; + }; + DebugObject d_obj; +} BThreadWork; + +/** + * Initializes the work dispatcher. + * Works may be started using {@link BThreadWork_Init}. + * + * @param o the object + * @param reactor reactor we live in + * @param num_threads_hint hint for the number of threads to use: + * <0 - A choice will be made automatically, probably based on the number of CPUs. + * 0 - No additional threads will be used, and computations will be performed directly + * in the event loop in job handlers. + * @return 1 on success, 0 on failure + */ +int BThreadWorkDispatcher_Init (BThreadWorkDispatcher *o, BReactor *reactor, int num_threads_hint) WARN_UNUSED; + +/** + * Frees the work dispatcher. + * There must be no {@link BThreadWork}'s with this dispatcher. + * + * @param o the object + */ +void BThreadWorkDispatcher_Free (BThreadWorkDispatcher *o); + +/** + * Determines whether threads are being used for computations, or computations + * are done in the event loop. + * + * @return 1 if threads are being used, 0 if not + */ +int BThreadWorkDispatcher_UsingThreads (BThreadWorkDispatcher *o); + +/** + * Initializes the work. + * + * @param o the object + * @param d work dispatcher + * @param handler_done handler to call when the work is done + * @param user argument to handler + * @param work_func function that will do the work, possibly from another thread + * @param work_func_user argument to work_func + */ +void BThreadWork_Init (BThreadWork *o, BThreadWorkDispatcher *d, BThreadWork_handler_done handler_done, void *user, BThreadWork_work_func work_func, void *work_func_user); + +/** + * Frees the work. + * After this function returns, the work function will either have fully executed, + * or not called at all, and never will be. + * + * @param o the object + */ +void BThreadWork_Free (BThreadWork *o); + +#endif diff --git a/external/badvpn_dns/threadwork/CMakeLists.txt b/external/badvpn_dns/threadwork/CMakeLists.txt new file mode 100644 index 00000000..5f223ae0 --- /dev/null +++ b/external/badvpn_dns/threadwork/CMakeLists.txt @@ -0,0 +1,6 @@ +set(BADVPN_THREADWORK_EXTRA_LIBS) +if (BADVPN_THREADWORK_USE_PTHREAD) + list(APPEND BADVPN_THREADWORK_EXTRA_LIBS pthread) +endif () + +badvpn_add_library(threadwork "system" "${BADVPN_THREADWORK_EXTRA_LIBS}" BThreadWork.c) diff --git a/external/badvpn_dns/tun2socks/CMakeLists.txt b/external/badvpn_dns/tun2socks/CMakeLists.txt new file mode 100644 index 00000000..8c8597cc --- /dev/null +++ b/external/badvpn_dns/tun2socks/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(badvpn-tun2socks + tun2socks.c + SocksUdpGwClient.c +) +target_link_libraries(badvpn-tun2socks system flow tuntap lwip socksclient udpgw_client) + +install( + TARGETS badvpn-tun2socks + RUNTIME DESTINATION bin +) + +install( + FILES badvpn-tun2socks.8 + DESTINATION share/man/man8 +) diff --git a/external/badvpn_dns/tun2socks/SocksUdpGwClient.c b/external/badvpn_dns/tun2socks/SocksUdpGwClient.c new file mode 100644 index 00000000..949d114b --- /dev/null +++ b/external/badvpn_dns/tun2socks/SocksUdpGwClient.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +static void free_socks (SocksUdpGwClient *o); +static void try_connect (SocksUdpGwClient *o); +static void reconnect_timer_handler (SocksUdpGwClient *o); +static void socks_client_handler (SocksUdpGwClient *o, int event); +static void udpgw_handler_servererror (SocksUdpGwClient *o); +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +static void free_socks (SocksUdpGwClient *o) +{ + ASSERT(o->have_socks) + + // disconnect udpgw client from SOCKS + if (o->socks_up) { + UdpGwClient_DisconnectServer(&o->udpgw_client); + } + + // free SOCKS client + BSocksClient_Free(&o->socks_client); + + // set have no SOCKS + o->have_socks = 0; +} + +static void try_connect (SocksUdpGwClient *o) +{ + ASSERT(!o->have_socks) + ASSERT(!BTimer_IsRunning(&o->reconnect_timer)) + + // init SOCKS client + if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { + BLog(BLOG_ERROR, "BSocksClient_Init failed"); + goto fail0; + } + + // set have SOCKS + o->have_socks = 1; + + // set SOCKS not up + o->socks_up = 0; + + return; + +fail0: + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void reconnect_timer_handler (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_socks) + + // try connecting + try_connect(o); +} + +static void socks_client_handler (SocksUdpGwClient *o, int event) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + + switch (event) { + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!o->socks_up) + + BLog(BLOG_INFO, "SOCKS up"); + + // connect udpgw client to SOCKS + if (!UdpGwClient_ConnectServer(&o->udpgw_client, BSocksClient_GetSendInterface(&o->socks_client), BSocksClient_GetRecvInterface(&o->socks_client))) { + BLog(BLOG_ERROR, "UdpGwClient_ConnectServer failed"); + goto fail0; + } + + // set SOCKS up + o->socks_up = 1; + + return; + + fail0: + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + case BSOCKSCLIENT_EVENT_ERROR: + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + BLog(BLOG_INFO, "SOCKS error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); + } break; + + default: ASSERT(0); + } +} + +static void udpgw_handler_servererror (SocksUdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_socks) + ASSERT(o->socks_up) + + BLog(BLOG_ERROR, "client reports server error"); + + // free SOCKS + free_socks(o); + + // set reconnect timer + BReactor_SetTimer(o->reactor, &o->reconnect_timer); +} + +static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + + // submit to user + o->handler_received(o->user, local_addr, remote_addr, data, data_len); + return; +} + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) +{ + // see asserts in UdpGwClient_Init + ASSERT(!BAddr_IsInvalid(&socks_server_addr)) + ASSERT(remote_udpgw_addr.type == BADDR_TYPE_IPV4 || remote_udpgw_addr.type == BADDR_TYPE_IPV6) + + // init arguments + o->udp_mtu = udp_mtu; + o->socks_server_addr = socks_server_addr; + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->remote_udpgw_addr = remote_udpgw_addr; + o->reactor = reactor; + o->user = user; + o->handler_received = handler_received; + + // init udpgw client + if (!UdpGwClient_Init(&o->udpgw_client, udp_mtu, max_connections, send_buffer_size, keepalive_time, o->reactor, o, + (UdpGwClient_handler_servererror)udpgw_handler_servererror, + (UdpGwClient_handler_received)udpgw_handler_received + )) { + goto fail0; + } + + // init reconnect timer + BTimer_Init(&o->reconnect_timer, reconnect_time, (BTimer_handler)reconnect_timer_handler, o); + + // set have no SOCKS + o->have_socks = 0; + + // try connecting + try_connect(o); + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; +} + +void SocksUdpGwClient_Free (SocksUdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + + // free SOCKS + if (o->have_socks) { + free_socks(o); + } + + // free reconnect timer + BReactor_RemoveTimer(o->reactor, &o->reconnect_timer); + + // free udpgw client + UdpGwClient_Free(&o->udpgw_client); +} + +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + // see asserts in UdpGwClient_SubmitPacket + + // submit to udpgw client + UdpGwClient_SubmitPacket(&o->udpgw_client, local_addr, remote_addr, is_dns, data, data_len); +} + diff --git a/external/badvpn_dns/tun2socks/SocksUdpGwClient.h b/external/badvpn_dns/tun2socks/SocksUdpGwClient.h new file mode 100644 index 00000000..217e0ec1 --- /dev/null +++ b/external/badvpn_dns/tun2socks/SocksUdpGwClient.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H +#define BADVPN_TUN2SOCKS_SOCKSUDPGWCLIENT_H + +#include +#include +#include +#include +#include + +typedef void (*SocksUdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +typedef struct { + int udp_mtu; + BAddr socks_server_addr; + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + BAddr remote_udpgw_addr; + BReactor *reactor; + void *user; + SocksUdpGwClient_handler_received handler_received; + UdpGwClient udpgw_client; + BTimer reconnect_timer; + int have_socks; + BSocksClient socks_client; + int socks_up; + DebugObject d_obj; +} SocksUdpGwClient; + +int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, + BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user, + SocksUdpGwClient_handler_received handler_received) WARN_UNUSED; +void SocksUdpGwClient_Free (SocksUdpGwClient *o); +void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); + +#endif diff --git a/external/badvpn_dns/tun2socks/badvpn-tun2socks.8 b/external/badvpn_dns/tun2socks/badvpn-tun2socks.8 new file mode 100644 index 00000000..d1ab50cd --- /dev/null +++ b/external/badvpn_dns/tun2socks/badvpn-tun2socks.8 @@ -0,0 +1,126 @@ +.TH badvpn-tun2socks 8 "February 2012" +.SH NAME +badvpn-tun2socks \- create a TUN device to route TCP traffic through a SOCKS server +.SH SYNOPSIS +.PP +.B +badvpn-tun2socks +.br + [\fB\-\-help\fR] +.br + [\fB\-\-version\fR] +.br + [\fB\-\-logger\fR ] +.br + [\fB\-\-syslog-facility\fR ] [\fB\-\-syslog-ident\fR ] +.br + [\fB\-\-loglevel\fR <0-5/none/error/warning/notice/info/debug>] +.br + [\fB\-\-channel-loglevel\fR <0-5/none/error/warning/notice/info/debug>] ... +.br + [\fB\-\-tundev\fR ] +.br + \fB\-\-netif\-ipaddr\fR +.br + \fB\-\-netif\-netmask\fR +.br + \fB\-\-socks\-server\-addr\fR +.br + [\fB\-\-udpgw-remote-server-addr\fR ] +.br + [\fB\-\-udpgw-max-connections\fR ] +.br + [\fB\-\-udpgw-connection-buffer-size\fR ] +.PP +Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6). +.SH DESCRIPTION +.PP +badvpn-tun2socks +is a network utility used to "socksify" TCP connections at the network +layer. It implements a TUN device which accepts all incoming TCP +connections (regardless of destination IP), and forwards them through +a SOCKS server. This allows you to forward all connections through +SOCKS, without any need for application support. It can be used, for +example, to forward connections through a remote SSH server. +.SH EXAMPLE +.PP +This example demonstrates using tun2socks in combination with SSH's dynamic forwarding feature. + +Connect to the SSH server, passing -D localhost:1080 to the ssh +command to enable dynamic forwarding. This will make ssh open a local +SOCKS server which tun2socks forward connection through. + +First create a TUN device (eg. using openvpn): + +.nf + openvpn --mktun --dev tun0 --user +.fi + +Configure the IP of the new tun device: + +.nf + ifconfig tun0 10.0.0.1 netmask 255.255.255.0 +.fi + +Now start the badvpn-tun2socks program: + +.nf + badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.0.0.2 --netif-netmask 255.255.255.0 \\ + --socks-server-addr 127.0.0.1:1080 +.fi + +Note that the address 10.0.0.2 is not a typo. It specifies the IP address of the virtual +router inside the TUN device, and must be different from the IP of the +TUN interface itself (but in the same subnet). + +Now you should be able to ping the virtual router's IP (10.0.0.2): + +.nf + ping -n 10.0.0.2 +.fi + +All that remains is to route connections through the TUN device +instead of the existing default gateway. This is done as follows: + +1. Add a route to the SSH server through your existing gateway, with a +lower metric than the original default route. + +2. If your DNS servers are in a network that is not direcly attached (e.g. in the Internet), +also add routes for them (like for the SSH server). This is +needed because tun2socks does not forward UDP by default (see below). + +3. Add a default route through the virtual router in the TUN device, +with a lower metric than the original default route, but higher than +the SSH and DNS routes. + +This will make all external connections go through the TUN device, +except for the SSH connection (else SSH would go through the TUN +device, which would go through... SSH). + +For example (assuming there are no existing default routes with metric +<=6; otherwise remove them or change their metrics): + +.nf + route add gw metric 5 + + route add default gw 10.0.0.2 metric 6 +.fi +.SH UDP FORWARDING +tun2socks can forward UDP, however this requires a forwarder daemon, badvpn-udpgw to run +on the remote SSH server: + +.nf + badvpn-udpgw --listen-addr 127.0.0.1:7300 +.fi + +Then tell tun2socks to forward UDP via the forwarder: + +.nf + --udpgw-remote-server-addr 127.0.0.1:7300 +.fi +.SH COPYRIGHT +.PP +Copyright \(co 2010 Ambroz Bizjak +.br +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/external/badvpn_dns/tun2socks/tun2socks.c b/external/badvpn_dns/tun2socks/tun2socks.c new file mode 100644 index 00000000..51c3fb93 --- /dev/null +++ b/external/badvpn_dns/tun2socks/tun2socks.c @@ -0,0 +1,2138 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +// PSIPHON +#include "jni.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +#define SYNC_DECL \ + BPending sync_mark; \ + +#define SYNC_FROMHERE \ + BPending_Init(&sync_mark, BReactor_PendingGroup(&ss), NULL, NULL); \ + BPending_Set(&sync_mark); + +#define SYNC_BREAK \ + BPending_Free(&sync_mark); + +#define SYNC_COMMIT \ + BReactor_Synchronize(&ss, &sync_mark.base); \ + BPending_Free(&sync_mark); + + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *tundev; + char *netif_ipaddr; + char *netif_netmask; + char *netif_ip6addr; + char *socks_server_addr; + char *username; + char *password; + char *password_file; + int append_source_to_username; + char *udpgw_remote_server_addr; + int udpgw_max_connections; + int udpgw_connection_buffer_size; + int udpgw_transparent_dns; + + // ==== PSIPHON ==== + int tun_fd; + int tun_mtu; + int set_signal; + // ==== PSIPHON ==== +} options; + +// TCP client +struct tcp_client { + dead_t dead; + dead_t dead_client; + LinkedList1Node list_node; + BAddr local_addr; + BAddr remote_addr; + struct tcp_pcb *pcb; + int client_closed; + uint8_t buf[TCP_WND]; + int buf_used; + char *socks_username; + BSocksClient socks_client; + int socks_up; + int socks_closed; + StreamPassInterface *socks_send_if; + StreamRecvInterface *socks_recv_if; + uint8_t socks_recv_buf[CLIENT_SOCKS_RECV_BUF_SIZE]; + int socks_recv_buf_used; + int socks_recv_buf_sent; + int socks_recv_waiting; + int socks_recv_tcp_pending; +}; + +// IP address of netif +BIPAddr netif_ipaddr; + +// netmask of netif +BIPAddr netif_netmask; + +// IP6 address of netif +struct ipv6_addr netif_ip6addr; + +// SOCKS server address +BAddr socks_server_addr; + +// allocated password file contents +uint8_t *password_file_contents; + +// SOCKS authentication information +struct BSocksClient_auth_info socks_auth_info[2]; +size_t socks_num_auth_info; + +// remote udpgw server addr, if provided +BAddr udpgw_remote_server_addr; + +// reactor +BReactor ss; + +// set to 1 by terminate +int quitting; + +// TUN device +BTap device; + +// device write buffer +uint8_t *device_write_buf; + +// device reading +SinglePacketBuffer device_read_buffer; +PacketPassInterface device_read_interface; + +// udpgw client +SocksUdpGwClient udpgw_client; +int udp_mtu; + +// TCP timer +BTimer tcp_timer; + +// job for initializing lwip +BPending lwip_init_job; + +// lwip netif +int have_netif; +struct netif netif; + +// lwip TCP listener +struct tcp_pcb *listener; + +// lwip TCP/IPv6 listener +struct tcp_pcb *listener_ip6; + +// TCP clients +LinkedList1 tcp_clients; + +// number of clients +int num_clients; + +// ==== PSIPHON ==== +static void run (void); +static void init_arguments (const char* program_name); +// ==== PSIPHON ==== + +static void terminate (void); +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder); +static void lwip_init_job_hadler (void *unused); +static void tcp_timer_handler (void *unused); +static void device_error_handler (void *unused); +static void device_read_handler_send (void *unused, uint8_t *data, int data_len); +static int process_device_udp_packet (uint8_t *data, int data_len); +static err_t netif_init_func (struct netif *netif); +static err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr); +static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +static err_t common_netif_output (struct netif *netif, struct pbuf *p); +static err_t netif_input_func (struct pbuf *p, struct netif *inp); +static void client_logfunc (struct tcp_client *client); +static void client_log (struct tcp_client *client, int level, const char *fmt, ...); +static err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err); +static void client_handle_freed_client (struct tcp_client *client); +static void client_free_client (struct tcp_client *client); +static void client_abort_client (struct tcp_client *client); +static void client_free_socks (struct tcp_client *client); +static void client_murder (struct tcp_client *client); +static void client_dealloc (struct tcp_client *client); +static void client_err_func (void *arg, err_t err); +static err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +static void client_socks_handler (struct tcp_client *client, int event); +static void client_send_to_socks (struct tcp_client *client); +static void client_socks_send_handler_done (struct tcp_client *client, int data_len); +static void client_socks_recv_initiate (struct tcp_client *client); +static void client_socks_recv_handler_done (struct tcp_client *client, int data_len); +static int client_socks_recv_send_out (struct tcp_client *client); +static err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len); +static void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + + +//==== PSIPHON ==== + + +JNIEnv* g_env = 0; + +void PsiphonLog(const char *levelStr, const char *channelStr, const char *msgStr) +{ + if (!g_env) + { + return; + } + // Note: we could cache the class and method references if log is called frequently + + jstring level = (*g_env)->NewStringUTF(g_env, levelStr); + jstring channel = (*g_env)->NewStringUTF(g_env, channelStr); + jstring msg = (*g_env)->NewStringUTF(g_env, msgStr); + + jclass cls = (*g_env)->FindClass(g_env, "org/torproject/android/vpn/Tun2Socks"); + jmethodID logMethod = (*g_env)->GetStaticMethodID(g_env, cls, "logTun2Socks", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + (*g_env)->CallStaticVoidMethod(g_env, cls, logMethod, level, channel, msg); + + (*g_env)->DeleteLocalRef(g_env, cls); + + (*g_env)->DeleteLocalRef(g_env, level); + (*g_env)->DeleteLocalRef(g_env, channel); + (*g_env)->DeleteLocalRef(g_env, msg); +} + +// org.torproject.android.vpn.Tun2Socks.runTun2Socks +JNIEXPORT jint JNICALL Java_org_torproject_android_vpn_Tun2Socks_runTun2Socks( + JNIEnv* env, + jclass cls, + jint vpnInterfaceFileDescriptor, + jint vpnInterfaceMTU, + jstring vpnIpAddress, + jstring vpnNetMask, + jstring socksServerAddress, + jstring udpgwServerAddress, + jint udpgwTransparentDNS) +{ + g_env = env; + + const char* vpnIpAddressStr = (*env)->GetStringUTFChars(env, vpnIpAddress, 0); + const char* vpnNetMaskStr = (*env)->GetStringUTFChars(env, vpnNetMask, 0); + const char* socksServerAddressStr = (*env)->GetStringUTFChars(env, socksServerAddress, 0); + const char* udpgwServerAddressStr = (*env)->GetStringUTFChars(env, udpgwServerAddress, 0); + + init_arguments("Drobot tun2socks"); + + options.netif_ipaddr = (char*)vpnIpAddressStr; + options.netif_netmask = (char*)vpnNetMaskStr; + options.socks_server_addr = (char*)socksServerAddressStr; + options.udpgw_remote_server_addr = (char*)udpgwServerAddressStr; + options.udpgw_transparent_dns = udpgwTransparentDNS; + options.tun_fd = vpnInterfaceFileDescriptor; + options.tun_mtu = vpnInterfaceMTU; + options.set_signal = 0; + options.loglevel = 4; + + BLog_InitPsiphon(); + + run(); + + (*env)->ReleaseStringUTFChars(env, vpnIpAddress, vpnIpAddressStr); + (*env)->ReleaseStringUTFChars(env, vpnNetMask, vpnNetMaskStr); + (*env)->ReleaseStringUTFChars(env, socksServerAddress, socksServerAddressStr); + (*env)->ReleaseStringUTFChars(env, udpgwServerAddress, udpgwServerAddressStr); + + g_env = 0; + + // TODO: return success/error + + return 1; +} + +JNIEXPORT jint JNICALL Java_org_torproject_android_vpn_Tun2Socks_terminateTun2Socks( + jclass cls, + JNIEnv* env) +{ + terminate(); + return 0; +} + +// from tcp_helper.c +/** Remove all pcbs on the given list. */ +static void tcp_remove(struct tcp_pcb* pcb_list) +{ + struct tcp_pcb *pcb = pcb_list; + struct tcp_pcb *pcb2; + + while(pcb != NULL) + { + pcb2 = pcb; + pcb = pcb->next; + tcp_abort(pcb2); + } +} + + + +void run() +{ + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // clear password contents pointer + password_file_contents = NULL; + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // init time + BTime_Init(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // set not quitting + quitting = 0; + + // PSIPHON + if (options.set_signal) { + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + } + + // PSIPHON + if (options.tun_fd) { + // use supplied file descriptor + if (!BTap_InitWithFD(&device, &ss, options.tun_fd, options.tun_mtu, device_error_handler, NULL, 1)) { + BLog(BLOG_ERROR, "BTap_InitWithFD failed"); + goto fail3; + } + } else { + // init TUN device + if (!BTap_Init(&device, &ss, options.tundev, device_error_handler, NULL, 1)) { + BLog(BLOG_ERROR, "BTap_Init failed"); + goto fail3; + } + } + + // NOTE: the order of the following is important: + // first device writing must evaluate, + // then lwip (so it can send packets to the device), + // then device reading (so it can pass received packets to lwip). + + // init device reading + PacketPassInterface_Init(&device_read_interface, BTap_GetMTU(&device), device_read_handler_send, NULL, BReactor_PendingGroup(&ss)); + if (!SinglePacketBuffer_Init(&device_read_buffer, BTap_GetOutput(&device), &device_read_interface, BReactor_PendingGroup(&ss))) { + BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail4; + } + + if (options.udpgw_remote_server_addr && !options.udpgw_transparent_dns) { + // compute maximum UDP payload size we need to pass through udpgw + udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); + if (options.netif_ip6addr) { + int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); + if (udp_mtu < udp_ip6_mtu) { + udp_mtu = udp_ip6_mtu; + } + } + if (udp_mtu < 0) { + udp_mtu = 0; + } + + // make sure our UDP payloads aren't too large for udpgw + int udpgw_mtu = udpgw_compute_mtu(udp_mtu); + if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { + BLog(BLOG_ERROR, "device MTU is too large for UDP"); + goto fail4a; + } + + // init udpgw client + if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, + socks_server_addr, socks_auth_info, socks_num_auth_info, + udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received + )) { + BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed"); + goto fail4a; + } + } + + // init lwip init job + BPending_Init(&lwip_init_job, BReactor_PendingGroup(&ss), lwip_init_job_hadler, NULL); + BPending_Set(&lwip_init_job); + + // init device write buffer + if (!(device_write_buf = (uint8_t *)BAlloc(BTap_GetMTU(&device)))) { + BLog(BLOG_ERROR, "BAlloc failed"); + goto fail5; + } + + // init TCP timer + // it won't trigger before lwip is initialized, becuase the lwip init is a job + BTimer_Init(&tcp_timer, TCP_TMR_INTERVAL, tcp_timer_handler, NULL); + BReactor_SetTimer(&ss, &tcp_timer); + + // set no netif + have_netif = 0; + + // set no listener + listener = NULL; + listener_ip6 = NULL; + + // init clients list + LinkedList1_Init(&tcp_clients); + + // init number of clients + num_clients = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + LinkedList1Node *node; + while (node = LinkedList1_GetFirst(&tcp_clients)) { + struct tcp_client *client = UPPER_OBJECT(node, struct tcp_client, list_node); + client_murder(client); + } + + // free listener + if (listener_ip6) { + tcp_close(listener_ip6); + } + if (listener) { + tcp_close(listener); + } + + // free netif + if (have_netif) { + netif_remove(&netif); + } + + // ==== PSIPHON ==== + // The existing tun2socks cleanup sometimes leaves some TCP connections + // in the TIME_WAIT state. With regular tun2socks, these will be cleaned up + // by process termination. Since we re-init tun2socks within one process, + // and tcp_bind_to_netif requires no TCP connections bound to the network + // interface, we need to explicitly clean these up. Since we're also closing + // both sources of tunneled packets (VPN fd and SOCKS sockets), there should + // be no need to keep these TCP connections in TIME_WAIT between tun2socks + // invocations. + // After further testing, we found at least one TCP connection left in the + // active list (with state SYN_RCVD). Now we're aborting the active list + // as well, and the bound list for good measure. + tcp_remove(tcp_bound_pcbs); + tcp_remove(tcp_active_pcbs); + tcp_remove(tcp_tw_pcbs); + // ==== PSIPHON ==== + + + BReactor_RemoveTimer(&ss, &tcp_timer); + BFree(device_write_buf); +fail5: + BPending_Free(&lwip_init_job); + if (options.udpgw_remote_server_addr && !options.udpgw_transparent_dns) { + SocksUdpGwClient_Free(&udpgw_client); + } +fail4a: + SinglePacketBuffer_Free(&device_read_buffer); +fail4: + PacketPassInterface_Free(&device_read_interface); + BTap_Free(&device); +fail3: + BSignal_Finish(); +fail2: + BReactor_Free(&ss); +fail1: + BFree(password_file_contents); + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + DebugObjectGlobal_Finish(); +} + +void terminate (void) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "tearing down"); + + // set quitting + quitting = 1; + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--tundev ]\n" + " --netif-ipaddr \n" + " --netif-netmask \n" + " --socks-server-addr \n" + " [--netif-ip6addr ]\n" + " [--username ]\n" + " [--password ]\n" + " [--password-file ]\n" + " [--append-source-to-username]\n" + " [--udpgw-remote-server-addr ]\n" + " [--udpgw-max-connections ]\n" + " [--udpgw-connection-buffer-size ]\n" + " [--udpgw-transparent-dns]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +//==== PSIPHON ==== + +void init_arguments (const char* program_name) +{ + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = (char*)program_name; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.tundev = NULL; + options.netif_ipaddr = NULL; + options.netif_netmask = NULL; + options.netif_ip6addr = NULL; + options.socks_server_addr = NULL; + options.username = NULL; + options.password = NULL; + options.password_file = NULL; + options.append_source_to_username = 0; + options.udpgw_remote_server_addr = NULL; + options.udpgw_max_connections = DEFAULT_UDPGW_MAX_CONNECTIONS; + options.udpgw_connection_buffer_size = DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE; + options.udpgw_transparent_dns = 0; + + options.tun_fd = 0; + options.set_signal = 1; +} + +//==== PSIPHON ==== + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + // PSIPHON + init_arguments(argv[0]); + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--tundev")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.tundev = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-ipaddr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ipaddr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-netmask")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_netmask = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--netif-ip6addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.netif_ip6addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--socks-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.socks_server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--username")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.username = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--password-file")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.password_file = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--append-source-to-username")) { + options.append_source_to_username = 1; + } + else if (!strcmp(arg, "--udpgw-remote-server-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.udpgw_remote_server_addr = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--udpgw-max-connections")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_max_connections = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--udpgw-connection-buffer-size")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udpgw_connection_buffer_size = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--udpgw-transparent-dns")) { + options.udpgw_transparent_dns = 1; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (!options.netif_ipaddr) { + fprintf(stderr, "--netif-ipaddr is required\n"); + return 0; + } + + if (!options.netif_netmask) { + fprintf(stderr, "--netif-netmask is required\n"); + return 0; + } + + if (!options.socks_server_addr) { + fprintf(stderr, "--socks-server-addr is required\n"); + return 0; + } + + if (options.username) { + if (!options.password && !options.password_file) { + fprintf(stderr, "username given but password not given\n"); + return 0; + } + + if (options.password && options.password_file) { + fprintf(stderr, "--password and --password-file cannot both be given\n"); + return 0; + } + } + + return 1; +} + +int process_arguments (void) +{ + ASSERT(!password_file_contents) + + // resolve netif ipaddr + if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) { + BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed"); + return 0; + } + if (netif_ipaddr.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif ipaddr: must be an IPv4 address"); + return 0; + } + + // resolve netif netmask + if (!BIPAddr_Resolve(&netif_netmask, options.netif_netmask, 0)) { + BLog(BLOG_ERROR, "netif netmask: BIPAddr_Resolve failed"); + return 0; + } + if (netif_netmask.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "netif netmask: must be an IPv4 address"); + return 0; + } + + // parse IP6 address + if (options.netif_ip6addr) { + if (!ipaddr6_parse_ipv6_addr(options.netif_ip6addr, &netif_ip6addr)) { + BLog(BLOG_ERROR, "netif ip6addr: incorrect"); + return 0; + } + } + + // resolve SOCKS server address + if (!BAddr_Parse2(&socks_server_addr, options.socks_server_addr, NULL, 0, 0)) { + BLog(BLOG_ERROR, "socks server addr: BAddr_Parse2 failed"); + return 0; + } + + // add none socks authentication method + socks_auth_info[0] = BSocksClient_auth_none(); + socks_num_auth_info = 1; + + // add password socks authentication method + if (options.username) { + const char *password; + size_t password_len; + if (options.password) { + password = options.password; + password_len = strlen(options.password); + } else { + if (!read_file(options.password_file, &password_file_contents, &password_len)) { + BLog(BLOG_ERROR, "failed to read password file"); + return 0; + } + password = (char *)password_file_contents; + } + + socks_auth_info[socks_num_auth_info++] = BSocksClient_auth_password( + options.username, strlen(options.username), + password, password_len + ); + } + + // resolve remote udpgw server address + if (options.udpgw_remote_server_addr) { + if (!BAddr_Parse2(&udpgw_remote_server_addr, options.udpgw_remote_server_addr, NULL, 0, 0)) { + BLog(BLOG_ERROR, "remote udpgw server addr: BAddr_Parse2 failed"); + return 0; + } + } + + return 1; +} + +void signal_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_NOTICE, "termination requested"); + + terminate(); +} + +BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder) +{ + BAddr addr; + if (is_ipv6) { + BAddr_InitIPv6(&addr, (uint8_t *)ipx_addr->ip6.addr, hton16(port_hostorder)); + } else { + BAddr_InitIPv4(&addr, ipx_addr->ip4.addr, hton16(port_hostorder)); + } + return addr; +} + +void lwip_init_job_hadler (void *unused) +{ + ASSERT(!quitting) + ASSERT(netif_ipaddr.type == BADDR_TYPE_IPV4) + ASSERT(netif_netmask.type == BADDR_TYPE_IPV4) + ASSERT(!have_netif) + ASSERT(!listener) + ASSERT(!listener_ip6) + + BLog(BLOG_DEBUG, "lwip init"); + + // NOTE: the device may fail during this, but there's no harm in not checking + // for that at every step + + // init lwip + lwip_init(); + + // make addresses for netif + ip_addr_t addr; + addr.addr = netif_ipaddr.ipv4; + ip_addr_t netmask; + netmask.addr = netif_netmask.ipv4; + ip_addr_t gw; + ip_addr_set_any(&gw); + + // init netif + if (!netif_add(&netif, &addr, &netmask, &gw, NULL, netif_init_func, netif_input_func)) { + BLog(BLOG_ERROR, "netif_add failed"); + goto fail; + } + have_netif = 1; + + // set netif up + netif_set_up(&netif); + + // set netif pretend TCP + netif_set_pretend_tcp(&netif, 1); + + // set netif default + netif_set_default(&netif); + + if (options.netif_ip6addr) { + // add IPv6 address + memcpy(netif_ip6_addr(&netif, 0), netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes)); + netif_ip6_addr_set_state(&netif, 0, IP6_ADDR_VALID); + } + + // init listener + struct tcp_pcb *l = tcp_new(); + if (!l) { + BLog(BLOG_ERROR, "tcp_new failed"); + goto fail; + } + + // bind listener + if (tcp_bind_to_netif(l, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l); + goto fail; + } + + // listen listener + if (!(listener = tcp_listen(l))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l); + goto fail; + } + + // setup listener accept handler + tcp_accept(listener, listener_accept_func); + + if (options.netif_ip6addr) { + struct tcp_pcb *l_ip6 = tcp_new_ip6(); + if (!l_ip6) { + BLog(BLOG_ERROR, "tcp_new_ip6 failed"); + goto fail; + } + + if (tcp_bind_to_netif(l_ip6, "ho0") != ERR_OK) { + BLog(BLOG_ERROR, "tcp_bind_to_netif failed"); + tcp_close(l_ip6); + goto fail; + } + + if (!(listener_ip6 = tcp_listen(l_ip6))) { + BLog(BLOG_ERROR, "tcp_listen failed"); + tcp_close(l_ip6); + goto fail; + } + + tcp_accept(listener_ip6, listener_accept_func); + } + + return; + +fail: + if (!quitting) { + terminate(); + } +} + +void tcp_timer_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_DEBUG, "TCP timer"); + + // schedule next timer + // TODO: calculate timeout so we don't drift + BReactor_SetTimer(&ss, &tcp_timer); + + tcp_tmr(); + return; +} + +void device_error_handler (void *unused) +{ + ASSERT(!quitting) + + BLog(BLOG_ERROR, "device error"); + + terminate(); + return; +} + +void device_read_handler_send (void *unused, uint8_t *data, int data_len) +{ + ASSERT(!quitting) + ASSERT(data_len >= 0) + + BLog(BLOG_DEBUG, "device: received packet"); + + // accept packet + PacketPassInterface_Done(&device_read_interface); + + // process DNS directly + if (process_device_dns_packet(data, data_len)) { + BLog(BLOG_INFO, "end processing dns packet"); + return; + } + + // process UDP directly + if (process_device_udp_packet(data, data_len)) { + return; + } + + // obtain pbuf + if (data_len > UINT16_MAX) { + BLog(BLOG_WARNING, "device read: packet too large"); + return; + } + struct pbuf *p = pbuf_alloc(PBUF_RAW, data_len, PBUF_POOL); + if (!p) { + BLog(BLOG_WARNING, "device read: pbuf_alloc failed"); + return; + } + + // write packet to pbuf + ASSERT_FORCE(pbuf_take(p, data, data_len) == ERR_OK) + + // pass pbuf to input + if (netif.input(p, &netif) != ERR_OK) { + BLog(BLOG_WARNING, "device read: input failed"); + pbuf_free(p); + } +} + +int process_device_dns_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have dnsgw + if (!options.udpgw_remote_server_addr || !options.udpgw_transparent_dns) { + BLog(BLOG_WARNING, "No dnsgw to process dns packet"); + goto fail; + } + + static BAddr local_addr; + static BAddr remote_addr; + static int init = 0; + + int to_dns; + int from_dns; + int packet_length = 0; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + // to port 53 is considered a DNS packet + to_dns = udp_header.dest_port == hton16(53); + + // from port 8153 is considered a DNS packet + from_dns = udp_header.source_port == udpgw_remote_server_addr.ipv4.port; + + // if not DNS packet, just bypass it. + if (!to_dns && !from_dns) { + BLog(BLOG_WARNING, "No to_dns and from_dns packet: bypass"); + goto fail; + } + + // modify DNS packet + if (to_dns) { + BLog(BLOG_INFO, "UDP: to DNS %d bytes", data_len); + + // construct addresses + if (!init) { + init = 1; + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + } + + // build IP header + ipv4_header.destination_address = udpgw_remote_server_addr.ipv4.ip; + ipv4_header.source_address = netif_ipaddr.ipv4; + + // build UDP header + udp_header.dest_port = udpgw_remote_server_addr.ipv4.port; + + } else if (from_dns) { + + // if not initialized + if (!init) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from DNS %d bytes", data_len); + + // build IP header + ipv4_header.source_address = remote_addr.ipv4.ip; + ipv4_header.destination_address = local_addr.ipv4.ip; + + // build UDP header + udp_header.source_port = remote_addr.ipv4.port; + + } + + // update IPv4 header's checksum + ipv4_header.checksum = hton16(0); + ipv4_header.checksum = ipv4_checksum(&ipv4_header, NULL, 0); + + // update UDP header's checksum + udp_header.checksum = hton16(0); + udp_header.checksum = udp_checksum(&udp_header, data, data_len, + ipv4_header.source_address, ipv4_header.destination_address); + + // write packet + memcpy(device_write_buf, &ipv4_header, sizeof(ipv4_header)); + memcpy(device_write_buf + sizeof(ipv4_header), &udp_header, sizeof(udp_header)); + memcpy(device_write_buf + sizeof(ipv4_header) + sizeof(udp_header), data, data_len); + packet_length = sizeof(ipv4_header) + sizeof(udp_header) + data_len; + + } break; + + case 6: { + // TODO: support IPv6 DNS Gateway + goto fail; + } break; + + default: { + goto fail; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); + + return 1; + +fail: + return 0; +} + +int process_device_udp_packet (uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + + // do nothing if we don't have udpgw + if (!options.udpgw_remote_server_addr || options.udpgw_transparent_dns) { + goto fail; + } + + BAddr local_addr; + BAddr remote_addr; + int is_dns; + + uint8_t ip_version = 0; + if (data_len > 0) { + ip_version = (data[0] >> 4); + } + + switch (ip_version) { + case 4: { + // ignore non-UDP packets + if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) { + goto fail; + } + + // parse IPv4 header + struct ipv4_header ipv4_header; + if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); + // BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + + // if transparent DNS is enabled, any packet arriving at out netif + // address to port 53 is considered a DNS packet + is_dns = (options.udpgw_transparent_dns && + ipv4_header.destination_address == netif_ipaddr.ipv4 && + udp_header.dest_port == hton16(53)); + + if (is_dns) + {//change DNS port to 5400 for Orbot Tor access + + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address,udp_header.dest_port); + //BAddr_InitIPv4(&remote_addr, ipv4_header.source_address,hton16(5400)); + + } + else + { + BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); + + } + + } break; + + case 6: { + // ignore if IPv6 support is disabled + if (!options.netif_ip6addr) { + goto fail; + } + + // ignore non-UDP packets + if (data_len < sizeof(struct ipv6_header) || data[offsetof(struct ipv6_header, next_header)] != IPV6_NEXT_UDP) { + goto fail; + } + + // parse IPv6 header + struct ipv6_header ipv6_header; + if (!ipv6_check(data, data_len, &ipv6_header, &data, &data_len)) { + goto fail; + } + + // parse UDP + struct udp_header udp_header; + if (!udp_check(data, data_len, &udp_header, &data, &data_len)) { + goto fail; + } + + // verify UDP checksum + uint16_t checksum_in_packet = udp_header.checksum; + udp_header.checksum = 0; + uint16_t checksum_computed = udp_ip6_checksum(&udp_header, data, data_len, ipv6_header.source_address, ipv6_header.destination_address); + if (checksum_in_packet != checksum_computed) { + goto fail; + } + + BLog(BLOG_INFO, "UDP/IPv6: from device %d bytes", data_len); + + // construct addresses + BAddr_InitIPv6(&local_addr, ipv6_header.source_address, udp_header.source_port); + BAddr_InitIPv6(&remote_addr, ipv6_header.destination_address, udp_header.dest_port); + + // TODO dns + is_dns = 0; + } break; + + default: { + goto fail; + } break; + } + + // check payload length + if (data_len > udp_mtu) { + BLog(BLOG_ERROR, "packet is too large, cannot send to udpgw"); + goto fail; + } + + // submit packet to udpgw + SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, is_dns, data, data_len); + + return 1; + +fail: + return 0; +} + +err_t netif_init_func (struct netif *netif) +{ + BLog(BLOG_DEBUG, "netif func init"); + + netif->name[0] = 'h'; + netif->name[1] = 'o'; + netif->output = netif_output_func; + netif->output_ip6 = netif_output_ip6_func; + + return ERR_OK; +} + +err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + return common_netif_output(netif, p); +} + +err_t common_netif_output (struct netif *netif, struct pbuf *p) +{ + SYNC_DECL + + BLog(BLOG_DEBUG, "device write: send packet"); + + if (quitting) { + return ERR_OK; + } + + // if there is just one chunk, send it directly, else via buffer + if (!p->next) { + if (p->len > BTap_GetMTU(&device)) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + + SYNC_FROMHERE + BTap_Send(&device, (uint8_t *)p->payload, p->len); + SYNC_COMMIT + } else { + int len = 0; + do { + if (p->len > BTap_GetMTU(&device) - len) { + BLog(BLOG_WARNING, "netif func output: no space left"); + goto out; + } + memcpy(device_write_buf + len, p->payload, p->len); + len += p->len; + } while (p = p->next); + + SYNC_FROMHERE + BTap_Send(&device, device_write_buf, len); + SYNC_COMMIT + } + +out: + return ERR_OK; +} + +err_t netif_input_func (struct pbuf *p, struct netif *inp) +{ + uint8_t ip_version = 0; + if (p->len > 0) { + ip_version = (((uint8_t *)p->payload)[0] >> 4); + } + + switch (ip_version) { + case 4: { + return ip_input(p, inp); + } break; + case 6: { + if (options.netif_ip6addr) { + return ip6_input(p, inp); + } + } break; + } + + pbuf_free(p); + return ERR_OK; +} + +void client_logfunc (struct tcp_client *client) +{ + char local_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->local_addr, local_addr_s); + char remote_addr_s[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, remote_addr_s); + + BLog_Append("%05d (%s %s): ", num_clients, local_addr_s, remote_addr_s); +} + +void client_log (struct tcp_client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err) +{ + ASSERT(err == ERR_OK) + + // signal accepted + struct tcp_pcb *this_listener = (PCB_ISIPV6(newpcb) ? listener_ip6 : listener); + tcp_accepted(this_listener); + + // allocate client structure + struct tcp_client *client = (struct tcp_client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "listener accept: malloc failed"); + goto fail0; + } + client->socks_username = NULL; + + SYNC_DECL + SYNC_FROMHERE + + // read addresses + client->local_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->local_ip, newpcb->local_port); + client->remote_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->remote_ip, newpcb->remote_port); + + // get destination address + BAddr addr = client->local_addr; +#ifdef OVERRIDE_DEST_ADDR + ASSERT_FORCE(BAddr_Parse2(&addr, OVERRIDE_DEST_ADDR, NULL, 0, 1)) +#endif + + // add source address to username if requested + if (options.username && options.append_source_to_username) { + char addr_str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->remote_addr, addr_str); + client->socks_username = concat_strings(3, options.username, "@", addr_str); + if (!client->socks_username) { + goto fail1; + } + socks_auth_info[1].password.username = client->socks_username; + socks_auth_info[1].password.username_len = strlen(client->socks_username); + } + + // init SOCKS + if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info, + addr, (BSocksClient_handler)client_socks_handler, client, &ss)) { + BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed"); + goto fail1; + } + + // init dead vars + DEAD_INIT(client->dead); + DEAD_INIT(client->dead_client); + + // add to linked list + LinkedList1_Append(&tcp_clients, &client->list_node); + + // increment counter + ASSERT(num_clients >= 0) + num_clients++; + + // set pcb + client->pcb = newpcb; + + // set client not closed + client->client_closed = 0; + + // setup handler argument + tcp_arg(client->pcb, client); + + // setup handlers + tcp_err(client->pcb, client_err_func); + tcp_recv(client->pcb, client_recv_func); + + // setup buffer + client->buf_used = 0; + + // set SOCKS not up, not closed + client->socks_up = 0; + client->socks_closed = 0; + + client_log(client, BLOG_INFO, "accepted"); + + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + + return ERR_OK; + +fail1: + SYNC_BREAK + free(client->socks_username); + free(client); +fail0: + return ERR_MEM; +} + +void client_handle_freed_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // pcb was taken care of by the caller + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + + // if we have data to be sent to SOCKS and can send it, keep sending + if (client->buf_used > 0 && !client->socks_closed) { + client_log(client, BLOG_INFO, "waiting untill buffered data is sent to SOCKS"); + } else { + if (!client->socks_closed) { + client_free_socks(client); + } else { + client_dealloc(client); + } + } +} + +void client_free_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + err_t err = tcp_close(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_ERROR, "tcp_close failed (%d)", err); + tcp_abort(client->pcb); + } + + client_handle_freed_client(client); +} + +void client_abort_client (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // free pcb + tcp_abort(client->pcb); + + client_handle_freed_client(client); +} + +void client_free_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + + // stop sending to SOCKS + if (client->socks_up) { + // stop receiving from client + if (!client->client_closed) { + tcp_recv(client->pcb, NULL); + } + } + + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + + // if we have data to be sent to the client and we can send it, keep sending + if (client->socks_up && (client->socks_recv_buf_used >= 0 || client->socks_recv_tcp_pending > 0) && !client->client_closed) { + client_log(client, BLOG_INFO, "waiting until buffered data is sent to client"); + } else { + if (!client->client_closed) { + client_free_client(client); + } else { + client_dealloc(client); + } + } +} + +void client_murder (struct tcp_client *client) +{ + // free client + if (!client->client_closed) { + // remove callbacks + tcp_err(client->pcb, NULL); + tcp_recv(client->pcb, NULL); + tcp_sent(client->pcb, NULL); + + // abort + tcp_abort(client->pcb); + + // kill client dead var + DEAD_KILL(client->dead_client); + + // set client closed + client->client_closed = 1; + } + + // free SOCKS + if (!client->socks_closed) { + // free SOCKS + BSocksClient_Free(&client->socks_client); + + // set SOCKS closed + client->socks_closed = 1; + } + + // dealloc entry + client_dealloc(client); +} + +void client_dealloc (struct tcp_client *client) +{ + ASSERT(client->client_closed) + ASSERT(client->socks_closed) + + // decrement counter + ASSERT(num_clients > 0) + num_clients--; + + // remove client entry + LinkedList1_Remove(&tcp_clients, &client->list_node); + + // kill dead var + DEAD_KILL(client->dead); + + // free memory + free(client->socks_username); + free(client); +} + +void client_err_func (void *arg, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + + client_log(client, BLOG_INFO, "client error (%d)", (int)err); + + // the pcb was taken care of by the caller + client_handle_freed_client(client); +} + +err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct tcp_client *client = (struct tcp_client *)arg; + ASSERT(!client->client_closed) + ASSERT(err == ERR_OK) // checked in lwIP source. Otherwise, I've no idea what should + // be done with the pbuf in case of an error. + + if (!p) { + client_log(client, BLOG_INFO, "client closed"); + client_free_client(client); + return ERR_ABRT; + } + + ASSERT(p->tot_len > 0) + + // check if we have enough buffer + if (p->tot_len > sizeof(client->buf) - client->buf_used) { + client_log(client, BLOG_ERROR, "no buffer for data !?!"); + return ERR_MEM; + } + + // copy data to buffer + ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len) + client->buf_used += p->tot_len; + + // if there was nothing in the buffer before, and SOCKS is up, start send data + if (client->buf_used == p->tot_len && client->socks_up) { + ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed + + SYNC_DECL + SYNC_FROMHERE + client_send_to_socks(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + // free pbuff + pbuf_free(p); + + return ERR_OK; +} + +void client_socks_handler (struct tcp_client *client, int event) +{ + ASSERT(!client->socks_closed) + + switch (event) { + case BSOCKSCLIENT_EVENT_ERROR: { + client_log(client, BLOG_INFO, "SOCKS error"); + + client_free_socks(client); + } break; + + case BSOCKSCLIENT_EVENT_UP: { + ASSERT(!client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS up"); + + // init sending + client->socks_send_if = BSocksClient_GetSendInterface(&client->socks_client); + StreamPassInterface_Sender_Init(client->socks_send_if, (StreamPassInterface_handler_done)client_socks_send_handler_done, client); + + // init receiving + client->socks_recv_if = BSocksClient_GetRecvInterface(&client->socks_client); + StreamRecvInterface_Receiver_Init(client->socks_recv_if, (StreamRecvInterface_handler_done)client_socks_recv_handler_done, client); + client->socks_recv_buf_used = -1; + client->socks_recv_tcp_pending = 0; + if (!client->client_closed) { + tcp_sent(client->pcb, client_sent_func); + } + + // set up + client->socks_up = 1; + + // start sending data if there is any + if (client->buf_used > 0) { + client_send_to_socks(client); + } + + // start receiving data if client is still up + if (!client->client_closed) { + client_socks_recv_initiate(client); + } + } break; + + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + ASSERT(client->socks_up) + + client_log(client, BLOG_INFO, "SOCKS closed"); + + client_free_socks(client); + } break; + + default: + ASSERT(0); + } +} + +void client_send_to_socks (struct tcp_client *client) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + + // schedule sending + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); +} + +void client_socks_send_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->buf_used > 0) + ASSERT(data_len > 0) + ASSERT(data_len <= client->buf_used) + + // remove sent data from buffer + memmove(client->buf, client->buf + data_len, client->buf_used - data_len); + client->buf_used -= data_len; + + if (!client->client_closed) { + // confirm sent data + tcp_recved(client->pcb, data_len); + } + + if (client->buf_used > 0) { + // send any further data + StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used); + } + else if (client->client_closed) { + // client was closed we've sent everything we had buffered; we're done with it + client_log(client, BLOG_INFO, "removing after client went down"); + + client_free_socks(client); + } +} + +void client_socks_recv_initiate (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + StreamRecvInterface_Receiver_Recv(client->socks_recv_if, client->socks_recv_buf, sizeof(client->socks_recv_buf)); +} + +void client_socks_recv_handler_done (struct tcp_client *client, int data_len) +{ + ASSERT(data_len > 0) + ASSERT(data_len <= sizeof(client->socks_recv_buf)) + ASSERT(!client->socks_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used == -1) + + // if client was closed, stop receiving + if (client->client_closed) { + return; + } + + // set amount of data in buffer + client->socks_recv_buf_used = data_len; + client->socks_recv_buf_sent = 0; + client->socks_recv_waiting = 0; + + // send to client + if (client_socks_recv_send_out(client) < 0) { + return; + } + + // continue receiving if needed + if (client->socks_recv_buf_used == -1) { + client_socks_recv_initiate(client); + } +} + +int client_socks_recv_send_out (struct tcp_client *client) +{ + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(client->socks_recv_buf_used > 0) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + ASSERT(!client->socks_recv_waiting) + + // return value -1 means tcp_abort() was done, + // 0 means it wasn't and the client (pcb) is still up + + do { + int to_write = bmin_int(client->socks_recv_buf_used - client->socks_recv_buf_sent, tcp_sndbuf(client->pcb)); + if (to_write == 0) { + break; + } + + err_t err = tcp_write(client->pcb, client->socks_recv_buf + client->socks_recv_buf_sent, to_write, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + if (err == ERR_MEM) { + break; + } + + client_log(client, BLOG_INFO, "tcp_write failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + client->socks_recv_buf_sent += to_write; + client->socks_recv_tcp_pending += to_write; + } while (client->socks_recv_buf_sent < client->socks_recv_buf_used); + + // start sending now + err_t err = tcp_output(client->pcb); + if (err != ERR_OK) { + client_log(client, BLOG_INFO, "tcp_output failed (%d)", (int)err); + + client_abort_client(client); + return -1; + } + + // more data to queue? + if (client->socks_recv_buf_sent < client->socks_recv_buf_used) { + if (client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_ERROR, "can't queue data, but all data was confirmed !?!"); + + client_abort_client(client); + return -1; + } + + // set waiting, continue in client_sent_func + client->socks_recv_waiting = 1; + return 0; + } + + // everything was queued + client->socks_recv_buf_used = -1; + + return 0; +} + +err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + struct tcp_client *client = (struct tcp_client *)arg; + + ASSERT(!client->client_closed) + ASSERT(client->socks_up) + ASSERT(len > 0) + ASSERT(len <= client->socks_recv_tcp_pending) + + // decrement pending + client->socks_recv_tcp_pending -= len; + + // continue queuing + if (client->socks_recv_buf_used > 0) { + ASSERT(client->socks_recv_waiting) + ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used) + + // set not waiting + client->socks_recv_waiting = 0; + + // possibly send more data + if (client_socks_recv_send_out(client) < 0) { + return ERR_ABRT; + } + + // we just queued some data, so it can't have been confirmed yet + ASSERT(client->socks_recv_tcp_pending > 0) + + // continue receiving if needed + if (client->socks_recv_buf_used == -1 && !client->socks_closed) { + SYNC_DECL + SYNC_FROMHERE + client_socks_recv_initiate(client); + DEAD_ENTER(client->dead_client) + SYNC_COMMIT + DEAD_LEAVE2(client->dead_client) + if (DEAD_KILLED) { + return ERR_ABRT; + } + } + + return ERR_OK; + } + + // have we sent everything after SOCKS was closed? + if (client->socks_closed && client->socks_recv_tcp_pending == 0) { + client_log(client, BLOG_INFO, "removing after SOCKS went down"); + client_free_client(client); + return ERR_ABRT; + } + + return ERR_OK; +} + +void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + ASSERT(options.udpgw_remote_server_addr) + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(local_addr.type == remote_addr.type) + ASSERT(data_len >= 0) + + int packet_length = 0; + + switch (local_addr.type) { + case BADDR_TYPE_IPV4: { + BLog(BLOG_INFO, "UDP: from udpgw %d bytes", data_len); + + if (data_len > UINT16_MAX - (sizeof(struct ipv4_header) + sizeof(struct udp_header)) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP: packet is too large"); + return; + } + + // build IP header + struct ipv4_header iph; + iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph)); + iph.ds = hton8(0); + iph.total_length = hton16(sizeof(iph) + sizeof(struct udp_header) + data_len); + iph.identification = hton16(0); + iph.flags3_fragmentoffset13 = hton16(0); + iph.ttl = hton8(64); + iph.protocol = hton8(IPV4_PROTOCOL_UDP); + iph.checksum = hton16(0); + iph.source_address = remote_addr.ipv4.ip; + iph.destination_address = local_addr.ipv4.ip; + iph.checksum = ipv4_checksum(&iph, NULL, 0); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv4.port; + udph.dest_port = local_addr.ipv4.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + + case BADDR_TYPE_IPV6: { + BLog(BLOG_INFO, "UDP/IPv6: from udpgw %d bytes", data_len); + + if (!options.netif_ip6addr) { + BLog(BLOG_ERROR, "got IPv6 packet from udpgw but IPv6 is disabled"); + return; + } + + if (data_len > UINT16_MAX - sizeof(struct udp_header) || + data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)) + ) { + BLog(BLOG_ERROR, "UDP/IPv6: packet is too large"); + return; + } + + // build IPv6 header + struct ipv6_header iph; + iph.version4_tc4 = hton8((6 << 4)); + iph.tc4_fl4 = hton8(0); + iph.fl = hton16(0); + iph.payload_length = hton16(sizeof(struct udp_header) + data_len); + iph.next_header = hton8(IPV6_NEXT_UDP); + iph.hop_limit = hton8(64); + memcpy(iph.source_address, remote_addr.ipv6.ip, 16); + memcpy(iph.destination_address, local_addr.ipv6.ip, 16); + + // build UDP header + struct udp_header udph; + udph.source_port = remote_addr.ipv6.port; + udph.dest_port = local_addr.ipv6.port; + udph.length = hton16(sizeof(udph) + data_len); + udph.checksum = hton16(0); + udph.checksum = udp_ip6_checksum(&udph, data, data_len, iph.source_address, iph.destination_address); + + // write packet + memcpy(device_write_buf, &iph, sizeof(iph)); + memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph)); + memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len); + packet_length = sizeof(iph) + sizeof(udph) + data_len; + } break; + } + + // submit packet + BTap_Send(&device, device_write_buf, packet_length); +} diff --git a/external/badvpn_dns/tun2socks/tun2socks.h b/external/badvpn_dns/tun2socks/tun2socks.h new file mode 100644 index 00000000..caf57782 --- /dev/null +++ b/external/badvpn_dns/tun2socks/tun2socks.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Ambroz Bizjak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "tun2socks" + +// size of temporary buffer for passing data from the SOCKS server to TCP for sending +#define CLIENT_SOCKS_RECV_BUF_SIZE 8192 + +// maximum number of udpgw connections +#define DEFAULT_UDPGW_MAX_CONNECTIONS 256 + +// udpgw per-connection send buffer size, in number of packets +#define DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE 8 + +// udpgw reconnect time after connection fails +#define UDPGW_RECONNECT_TIME 5000 + +// udpgw keepalive sending interval +#define UDPGW_KEEPALIVE_TIME 10000 + +// option to override the destination addresses to give the SOCKS server +//#define OVERRIDE_DEST_ADDR "10.111.0.2:2000" diff --git a/external/badvpn_dns/tunctl/CMakeLists.txt b/external/badvpn_dns/tunctl/CMakeLists.txt new file mode 100644 index 00000000..4cbebc80 --- /dev/null +++ b/external/badvpn_dns/tunctl/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(badvpn-tunctl tunctl.c) + +install( + TARGETS badvpn-tunctl + RUNTIME DESTINATION bin +) diff --git a/external/badvpn_dns/tunctl/tunctl.c b/external/badvpn_dns/tunctl/tunctl.c new file mode 100644 index 00000000..4490adc3 --- /dev/null +++ b/external/badvpn_dns/tunctl/tunctl.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM_NAME "tunctl" + +#define TUN_DEVNODE "/dev/net/tun" + +struct { + int help; + int version; + int op; + char *device_name; + char *user; + char *group; +} options; + +#define OP_MKTUN 1 +#define OP_MKTAP 2 +#define OP_RMTUN 3 +#define OP_RMTAP 4 + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group); +static int remove_tuntap (const char *ifname, int is_tun); + +int main (int argc, char *argv[]) +{ + int res = 1; + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Error: Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + if (options.op == OP_MKTUN || options.op == OP_MKTAP) { + if (!options.user && !options.group) { + fprintf(stderr, "WARNING: with neither --user nor --group, anyone will be able to use the device!\n"); + } + res = !make_tuntap(options.device_name, options.op == OP_MKTUN, options.user, options.group); + } else { + res = !remove_tuntap(options.device_name, options.op == OP_RMTUN); + } + +fail0: + return res; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s [--help] [--version]\n" + " %s --mktun [--user ] [--group ]\n" + " %s --mktap [--user ] [--group ]\n" + " %s --rmtun \n" + " %s --rmtap \n", + name, name, name, name, name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.op = -1; + options.device_name = NULL; + options.user = NULL; + options.group = NULL; + + for (int i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--mktun")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_MKTUN; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--mktap")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_MKTAP; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--rmtun")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_RMTUN; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--rmtap")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.op >= 0) { + fprintf(stderr, "%s: can only do one operation\n", arg); + return 0; + } + options.op = OP_RMTAP; + options.device_name = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--user")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.user = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--group")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.group = argv[i + 1]; + i++; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + if (options.op < 0) { + fprintf(stderr, "--mktun, --mktap --rmtun or --rmtap is required\n"); + return 0; + } + + if ((options.user || options.group) && options.op != OP_MKTUN && options.op != OP_MKTAP) { + fprintf(stderr, "--user and --group only make sense for --mktun and --mktap\n"); + return 0; + } + + return 1; +} + +static int make_tuntap (const char *ifname, int is_tun, const char *user, const char *group) +{ + int res = 0; + + if (strlen(ifname) >= IFNAMSIZ) { + fprintf(stderr, "Error: ifname too long\n"); + goto fail0; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + perror("open"); + fprintf(stderr, "Error: open tun failed\n"); + goto fail0; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + perror("ioctl(TUNSETIFF)"); + fprintf(stderr, "Error: TUNSETIFF failed\n"); + goto fail1; + } + + uid_t uid = -1; + gid_t gid = -1; + + if (user) { + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + fprintf(stderr, "Error: malloc failed\n"); + goto fail1; + } + + struct passwd pwd; + struct passwd *res; + getpwnam_r(user, &pwd, buf, bufsize, &res); + if (!res) { + fprintf(stderr, "Error: getpwnam_r failed\n"); + free(buf); + goto fail1; + } + + uid = pwd.pw_uid; + free(buf); + } + + if (group) { + long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); + if (bufsize < 0) { + bufsize = 16384; + } + + char *buf = malloc(bufsize); + if (!buf) { + fprintf(stderr, "Error: malloc failed\n"); + goto fail1; + } + + struct group grp; + struct group *res; + getgrnam_r(group, &grp, buf, bufsize, &res); + if (!res) { + fprintf(stderr, "Error: getgrnam_r failed\n"); + free(buf); + goto fail1; + } + + gid = grp.gr_gid; + free(buf); + } + + if (ioctl(fd, TUNSETOWNER, uid) < 0) { + perror("ioctl(TUNSETOWNER)"); + fprintf(stderr, "Error: TUNSETOWNER failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETGROUP, gid) < 0) { + perror("ioctl(TUNSETGROUP)"); + fprintf(stderr, "Error: TUNSETGROUP failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) { + perror("ioctl(TUNSETPERSIST)"); + fprintf(stderr, "Error: TUNSETPERSIST failed\n"); + goto fail1; + } + + res = 1; + +fail1: + close(fd); +fail0: + return res; +} + +static int remove_tuntap (const char *ifname, int is_tun) +{ + int res = 0; + + if (strlen(ifname) >= IFNAMSIZ) { + fprintf(stderr, "Error: ifname too long\n"); + goto fail0; + } + + int fd = open(TUN_DEVNODE, O_RDWR); + if (fd < 0) { + perror("open"); + fprintf(stderr, "Error: open tun failed\n"); + goto fail0; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = (is_tun ? IFF_TUN : IFF_TAP); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname); + + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { + perror("ioctl(TUNSETIFF)"); + fprintf(stderr, "Error: TUNSETIFF failed\n"); + goto fail1; + } + + if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) { + perror("ioctl(TUNSETPERSIST)"); + fprintf(stderr, "Error: TUNSETPERSIST failed\n"); + goto fail1; + } + + res = 1; + +fail1: + close(fd); +fail0: + return res; +} diff --git a/external/badvpn_dns/tuntap/BTap.c b/external/badvpn_dns/tuntap/BTap.c new file mode 100644 index 00000000..af12558b --- /dev/null +++ b/external/badvpn_dns/tuntap/BTap.c @@ -0,0 +1,631 @@ +/** + * @file BTap.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#ifdef BADVPN_USE_WINAPI + #include + #include + #include + #include + #include "wintap-common.h" + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #ifdef BADVPN_LINUX + #include + #endif + #ifdef BADVPN_FREEBSD + #include + #include + #endif +#endif + +#include + +#include + +#include + +static void report_error (BTap *o); +static void output_handler_recv (BTap *o, uint8_t *data); + +#ifdef BADVPN_USE_WINAPI + +static void recv_olap_handler (BTap *o, int event, DWORD bytes) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->output_packet) + ASSERT(event == BREACTOR_IOCP_EVENT_SUCCEEDED || event == BREACTOR_IOCP_EVENT_FAILED) + + // set no output packet + o->output_packet = NULL; + + if (event == BREACTOR_IOCP_EVENT_FAILED) { + BLog(BLOG_ERROR, "read operation failed"); + report_error(o); + return; + } + + ASSERT(bytes >= 0) + ASSERT(bytes <= o->frame_mtu) + + // done + PacketRecvInterface_Done(&o->output, bytes); +} + +#else + +static void fd_handler (BTap *o, int events) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + + if (events&(BREACTOR_ERROR|BREACTOR_HUP)) { + BLog(BLOG_WARNING, "device fd reports error?"); + } + + if (events&BREACTOR_READ) do { + ASSERT(o->output_packet) + + // try reading into the buffer + int bytes = read(o->fd, o->output_packet, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later + break; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + // set no output packet + o->output_packet = NULL; + + // update events + o->poll_events &= ~BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + + // inform receiver we finished the packet + PacketRecvInterface_Done(&o->output, bytes); + } while (0); +} + +#endif + +void report_error (BTap *o) +{ + DEBUGERROR(&o->d_err, o->handler_error(o->handler_error_user)); +} + +void output_handler_recv (BTap *o, uint8_t *data) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data) + ASSERT(!o->output_packet) + +#ifdef BADVPN_USE_WINAPI + + memset(&o->recv_olap.olap, 0, sizeof(o->recv_olap.olap)); + + // read + BOOL res = ReadFile(o->device, data, o->frame_mtu, NULL, &o->recv_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "ReadFile failed (%u)", GetLastError()); + report_error(o); + return; + } + + o->output_packet = data; + +#else + + // attempt read + int bytes = read(o->fd, data, o->frame_mtu); + if (bytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // retry later in fd_handler + // remember packet + o->output_packet = data; + // update events + o->poll_events |= BREACTOR_READ; + BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, o->poll_events); + return; + } + // report fatal error + report_error(o); + return; + } + + ASSERT_FORCE(bytes <= o->frame_mtu) + + PacketRecvInterface_Done(&o->output, bytes); + +#endif +} + +int BTap_Init (BTap *o, BReactor *reactor, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) +{ + ASSERT(tun == 0 || tun == 1) + + struct BTap_init_data init_data; + init_data.dev_type = tun ? BTAP_DEV_TUN : BTAP_DEV_TAP; + init_data.init_type = BTAP_INIT_STRING; + init_data.init.string = devname; + + return BTap_Init2(o, reactor, init_data, handler_error, handler_error_user); +} + +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) +{ + ASSERT(init_data.dev_type == BTAP_DEV_TUN || init_data.dev_type == BTAP_DEV_TAP) + + // init arguments + o->reactor = reactor; + o->handler_error = handler_error; + o->handler_error_user = handler_error_user; + + #ifdef BADVPN_USE_WINAPI + + ASSERT(init_data.init_type == BTAP_INIT_STRING) + + // parse device specification + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specification provided"); + goto fail0; + } + + char *device_component_id; + char *device_name; + uint32_t tun_addrs[3]; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!tapwin32_parse_tun_spec(init_data.init.string, &device_component_id, &device_name, tun_addrs)) { + BLog(BLOG_ERROR, "failed to parse TUN device specification"); + goto fail0; + } + } else { + if (!tapwin32_parse_tap_spec(init_data.init.string, &device_component_id, &device_name)) { + BLog(BLOG_ERROR, "failed to parse TAP device specification"); + goto fail0; + } + } + + // locate device path + + char device_path[TAPWIN32_MAX_REG_SIZE]; + + BLog(BLOG_INFO, "Looking for TAP-Win32 with component ID %s, name %s", device_component_id, device_name); + + if (!tapwin32_find_device(device_component_id, device_name, &device_path)) { + BLog(BLOG_ERROR, "Could not find device"); + goto fail1; + } + + // open device + + BLog(BLOG_INFO, "Opening device %s", device_path); + + o->device = CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, 0); + if (o->device == INVALID_HANDLE_VALUE) { + BLog(BLOG_ERROR, "CreateFile failed"); + goto fail1; + } + + // set TUN if needed + + DWORD len; + + if (init_data.dev_type == BTAP_DEV_TUN) { + if (!DeviceIoControl(o->device, TAP_IOCTL_CONFIG_TUN, tun_addrs, sizeof(tun_addrs), tun_addrs, sizeof(tun_addrs), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed"); + goto fail2; + } + } + + // get MTU + + ULONG umtu; + + if (!DeviceIoControl(o->device, TAP_IOCTL_GET_MTU, NULL, 0, &umtu, sizeof(umtu), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_GET_MTU) failed"); + goto fail2; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = umtu; + } else { + o->frame_mtu = umtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + // set connected + + ULONG upstatus = TRUE; + if (!DeviceIoControl(o->device, TAP_IOCTL_SET_MEDIA_STATUS, &upstatus, sizeof(upstatus), &upstatus, sizeof(upstatus), &len, NULL)) { + BLog(BLOG_ERROR, "DeviceIoControl(TAP_IOCTL_SET_MEDIA_STATUS) failed"); + goto fail2; + } + + BLog(BLOG_INFO, "Device opened"); + + // associate device with IOCP + + if (!CreateIoCompletionPort(o->device, BReactor_GetIOCPHandle(o->reactor), 0, 0)) { + BLog(BLOG_ERROR, "CreateIoCompletionPort failed"); + goto fail2; + } + + // init send olap + BReactorIOCPOverlapped_Init(&o->send_olap, o->reactor, o, NULL); + + // init recv olap + BReactorIOCPOverlapped_Init(&o->recv_olap, o->reactor, o, (BReactorIOCPOverlapped_handler)recv_olap_handler); + + free(device_name); + free(device_component_id); + + goto success; + +fail2: + ASSERT_FORCE(CloseHandle(o->device)) +fail1: + free(device_name); + free(device_component_id); +fail0: + return 0; + + #endif + + #if defined(BADVPN_LINUX) || defined(BADVPN_FREEBSD) + + o->close_fd = (init_data.init_type != BTAP_INIT_FD); + + switch (init_data.init_type) { + case BTAP_INIT_FD: { + ASSERT(init_data.init.fd.fd >= 0) + ASSERT(init_data.init.fd.mtu >= 0) + ASSERT(init_data.dev_type != BTAP_DEV_TAP || init_data.init.fd.mtu >= BTAP_ETHERNET_HEADER_LENGTH) + + o->fd = init_data.init.fd.fd; + o->frame_mtu = init_data.init.fd.mtu; + } break; + + case BTAP_INIT_STRING: { + char devname_real[IFNAMSIZ]; + + #ifdef BADVPN_LINUX + + // open device + + if ((o->fd = open("/dev/net/tun", O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // configure device + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags |= IFF_NO_PI; + if (init_data.dev_type == BTAP_DEV_TUN) { + ifr.ifr_flags |= IFF_TUN; + } else { + ifr.ifr_flags |= IFF_TAP; + } + if (init_data.init.string) { + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", init_data.init.string); + } + + if (ioctl(o->fd, TUNSETIFF, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + #ifdef BADVPN_FREEBSD + + if (init_data.dev_type == BTAP_DEV_TUN) { + BLog(BLOG_ERROR, "TUN not supported on FreeBSD"); + goto fail0; + } + + if (!init_data.init.string) { + BLog(BLOG_ERROR, "no device specified"); + goto fail0; + } + + // open device + + char devnode[10 + IFNAMSIZ]; + snprintf(devnode, sizeof(devnode), "/dev/%s", init_data.init.string); + + if ((o->fd = open(devnode, O_RDWR)) < 0) { + BLog(BLOG_ERROR, "error opening device"); + goto fail0; + } + + // get name + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + if (ioctl(o->fd, TAPGIFNAME, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error configuring device"); + goto fail1; + } + + strcpy(devname_real, ifr.ifr_name); + + #endif + + // get MTU + + // open dummy socket for ioctls + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + BLog(BLOG_ERROR, "socket failed"); + goto fail1; + } + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname_real); + + if (ioctl(sock, SIOCGIFMTU, (void *)&ifr) < 0) { + BLog(BLOG_ERROR, "error getting MTU"); + close(sock); + goto fail1; + } + + if (init_data.dev_type == BTAP_DEV_TUN) { + o->frame_mtu = ifr.ifr_mtu; + } else { + o->frame_mtu = ifr.ifr_mtu + BTAP_ETHERNET_HEADER_LENGTH; + } + + close(sock); + } break; + + default: ASSERT(0); + } + + // set non-blocking + if (fcntl(o->fd, F_SETFL, O_NONBLOCK) < 0) { + BLog(BLOG_ERROR, "cannot set non-blocking"); + goto fail1; + } + + // init file descriptor object + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + o->poll_events = 0; + + goto success; + +fail1: + if (o->close_fd) { + ASSERT_FORCE(close(o->fd) == 0) + } +fail0: + return 0; + + #endif + +success: + // init output + PacketRecvInterface_Init(&o->output, o->frame_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set no output packet + o->output_packet = NULL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; +} + +// ==== PSIPHON ==== + +int BTap_InitWithFD (BTap *o, BReactor *reactor, int fd, int mtu, BTap_handler_error handler_error, void *handler_error_user, int tun) +{ + ASSERT(tun == 0 || tun == 1) + + #ifndef BADVPN_LINUX + + return 0; + + #endif + + o->reactor = reactor; + o->handler_error = handler_error; + o->handler_error_user = handler_error_user; + o->frame_mtu = mtu; + o->fd = fd; + o->close_fd = 1; + + // TODO: use BTap_Init2? Still some different behavior (we don't want the fcntl block; we do want close to be called) + + // The following is identical to BTap_Init... + + // init file descriptor object + BFileDescriptor_Init(&o->bfd, o->fd, (BFileDescriptor_handler)fd_handler, o); + if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) { + BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed"); + goto fail1; + } + o->poll_events = 0; + + goto success; + +fail1: + if (o->close_fd) { + ASSERT_FORCE(close(o->fd) == 0) + } +fail0: + return 0; + +success: + // init output + PacketRecvInterface_Init(&o->output, o->frame_mtu, (PacketRecvInterface_handler_recv)output_handler_recv, o, BReactor_PendingGroup(o->reactor)); + + // set no output packet + o->output_packet = NULL; + + DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor)); + DebugObject_Init(&o->d_obj); + return 1; +} + +// ==== PSIPHON ==== + +void BTap_Free (BTap *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free output + PacketRecvInterface_Free(&o->output); + +#ifdef BADVPN_USE_WINAPI + + // cancel I/O + ASSERT_FORCE(CancelIo(o->device)) + + // wait receiving to finish + if (o->output_packet) { + BLog(BLOG_DEBUG, "waiting for receiving to finish"); + BReactorIOCPOverlapped_Wait(&o->recv_olap, NULL, NULL); + } + + // free recv olap + BReactorIOCPOverlapped_Free(&o->recv_olap); + + // free send olap + BReactorIOCPOverlapped_Free(&o->send_olap); + + // close device + ASSERT_FORCE(CloseHandle(o->device)) + +#else + + // free BFileDescriptor + BReactor_RemoveFileDescriptor(o->reactor, &o->bfd); + + if (o->close_fd) { + // close file descriptor + ASSERT_FORCE(close(o->fd) == 0) + } + +#endif +} + +int BTap_GetMTU (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return o->frame_mtu; +} + +void BTap_Send (BTap *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + ASSERT(data_len >= 0) + ASSERT(data_len <= o->frame_mtu) + +#ifdef BADVPN_USE_WINAPI + + // ignore frames without an Ethernet header, or we get errors in WriteFile + if (data_len < 14) { + return; + } + + memset(&o->send_olap.olap, 0, sizeof(o->send_olap.olap)); + + // write + BOOL res = WriteFile(o->device, data, data_len, NULL, &o->send_olap.olap); + if (res == FALSE && GetLastError() != ERROR_IO_PENDING) { + BLog(BLOG_ERROR, "WriteFile failed (%u)", GetLastError()); + return; + } + + // wait + int succeeded; + DWORD bytes; + BReactorIOCPOverlapped_Wait(&o->send_olap, &succeeded, &bytes); + + if (!succeeded) { + BLog(BLOG_ERROR, "write operation failed"); + } else { + ASSERT(bytes >= 0) + ASSERT(bytes <= data_len) + + if (bytes < data_len) { + BLog(BLOG_ERROR, "write operation didn't write everything"); + } + } + +#else + + int bytes = write(o->fd, data, data_len); + if (bytes < 0) { + // malformed packets will cause errors, ignore them and act like + // the packet was accepeted + } else { + if (bytes != data_len) { + BLog(BLOG_WARNING, "written %d expected %d", bytes, data_len); + } + } + +#endif +} + +PacketRecvInterface * BTap_GetOutput (BTap *o) +{ + DebugObject_Access(&o->d_obj); + + return &o->output; +} diff --git a/external/badvpn_dns/tuntap/BTap.h b/external/badvpn_dns/tuntap/BTap.h new file mode 100644 index 00000000..d9fa5247 --- /dev/null +++ b/external/badvpn_dns/tuntap/BTap.h @@ -0,0 +1,199 @@ +/** + * @file BTap.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * TAP device abstraction. + */ + +#ifndef BADVPN_TUNTAP_BTAP_H +#define BADVPN_TUNTAP_BTAP_H + +#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_LINUX) + defined(BADVPN_FREEBSD)) != 1 +#error Unknown TAP backend or too many TAP backends +#endif + +#include + +#ifdef BADVPN_USE_WINAPI +#else +#include +#endif + +#include +#include +#include +#include +#include + +#define BTAP_ETHERNET_HEADER_LENGTH 14 + +/** + * Handler called when an error occurs on the device. + * The object must be destroyed from the job context of this + * handler, and no further I/O may occur. + * + * @param user as in {@link BTap_Init} + */ +typedef void (*BTap_handler_error) (void *used); + +typedef struct { + BReactor *reactor; + BTap_handler_error handler_error; + void *handler_error_user; + int frame_mtu; + PacketRecvInterface output; + uint8_t *output_packet; + +#ifdef BADVPN_USE_WINAPI + HANDLE device; + BReactorIOCPOverlapped send_olap; + BReactorIOCPOverlapped recv_olap; +#else + int close_fd; + int fd; + BFileDescriptor bfd; + int poll_events; +#endif + + DebugError d_err; + DebugObject d_obj; +} BTap; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param devname name of the devece to open. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying a NULL devname is equivalent to specifying ":". + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @param tun whether to create a TUN (IP) device or a TAP (Ethernet) device. Must be 0 or 1. + * @return 1 on success, 0 on failure + */ +int BTap_Init (BTap *o, BReactor *bsys, char *devname, BTap_handler_error handler_error, void *handler_error_user, int tun) WARN_UNUSED; + +// PSIPHON +int BTap_InitWithFD (BTap *o, BReactor *bsys, int fd, int mtu, BTap_handler_error handler_error, void *handler_error_user, int tun) WARN_UNUSED; + +enum BTap_dev_type {BTAP_DEV_TUN, BTAP_DEV_TAP}; + +enum BTap_init_type { + BTAP_INIT_STRING, +#ifndef BADVPN_USE_WINAPI + BTAP_INIT_FD, +#endif +}; + +struct BTap_init_data { + enum BTap_dev_type dev_type; + enum BTap_init_type init_type; + union { + char *string; + struct { + int fd; + int mtu; + } fd; + } init; +}; + +/** + * Initializes the TAP device. + * + * @param o the object + * @param BReactor {@link BReactor} we live in + * @param init_data struct containing initialization parameters (to allow transparent passing). + * init.data.dev_type must be either BTAP_DEV_TUN for an IP device, or + * BTAP_DEV_TAP for an Ethernet device. + * init_data.init_type must be either BTAP_INIT_STRING or BTAP_INIT_FD. + * For BTAP_INIT_STRING, init_data.init.string specifies the TUN or TAP + * device, as described next. + * On Linux: a network interface name. If it is NULL, no + * specific device will be requested, and the operating system + * may create a new device. + * On Windows: a string "component_id:device_name", where + * component_id is a string identifying the driver, and device_name + * is the name of the network interface. If component_id is empty, + * a hardcoded default will be used instead. If device_name is empty, + * the first device found with a matching component_id will be used. + * Specifying NULL is equivalent to specifying ":". + * For BTAP_INIT_FD, the device is initialized using a file descriptor. + * In this case, init_data.init.fd.fd must be set to the file descriptor, + * and init_data.init.fd.mtu must be set to the largest IP packet or + * Ethernet frame supported, for a TUN or TAP device, respectively. + * File descriptor initialization is not supported on Windows. + * @param handler_error error handler function + * @param handler_error_user value passed to error handler + * @return 1 on success, 0 on failure + */ +int BTap_Init2 (BTap *o, BReactor *reactor, struct BTap_init_data init_data, BTap_handler_error handler_error, void *handler_error_user) WARN_UNUSED; + +/** + * Frees the TAP device. + * + * @param o the object + */ +void BTap_Free (BTap *o); + +/** + * Returns the device's maximum transmission unit (including any protocol headers). + * + * @param o the object + * @return device's MTU + */ +int BTap_GetMTU (BTap *o); + +/** + * Sends a packet to the device. + * Any errors will be reported via a job. + * + * @param o the object + * @param data packet to send + * @param data_len length of packet. Must be >=0 and <=MTU, as reported by {@link BTap_GetMTU}. + */ +void BTap_Send (BTap *o, uint8_t *data, int data_len); + +/** + * Returns a {@link PacketRecvInterface} for reading packets from the device. + * The MTU of the interface will be {@link BTap_GetMTU}. + * + * @param o the object + * @return output interface + */ +PacketRecvInterface * BTap_GetOutput (BTap *o); + +#endif diff --git a/external/badvpn_dns/tuntap/CMakeLists.txt b/external/badvpn_dns/tuntap/CMakeLists.txt new file mode 100644 index 00000000..fd451c1a --- /dev/null +++ b/external/badvpn_dns/tuntap/CMakeLists.txt @@ -0,0 +1,10 @@ +set(TUNTAP_ADDITIONAL_SOURCES) +if (WIN32) + list(APPEND TUNTAP_ADDITIONAL_SOURCES tapwin32-funcs.c) +endif () + +set(TUNTAP_SOURCES + BTap.c + ${TUNTAP_ADDITIONAL_SOURCES} +) +badvpn_add_library(tuntap "system;flow" "" "${TUNTAP_SOURCES}") diff --git a/external/badvpn_dns/tuntap/tapwin32-funcs.c b/external/badvpn_dns/tuntap/tapwin32-funcs.c new file mode 100644 index 00000000..37ce05d8 --- /dev/null +++ b/external/badvpn_dns/tuntap/tapwin32-funcs.c @@ -0,0 +1,227 @@ +/** + * @file tapwin32-funcs.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "wintap-common.h" + +#include + +static int split_spec (char *name, char *sep, char **out_fields[], int num_fields) +{ + ASSERT(num_fields > 0) + ASSERT(strlen(sep) > 0) + + size_t seplen = strlen(sep); + + int i = 0; + while (i < num_fields - 1) { + char *s = strstr(name, sep); + if (!s) { + DEBUG("missing separator number %d", (i + 1)); + goto fail; + } + + if (!(*out_fields[i] = b_strdup_bin(name, s - name))) { + DEBUG("b_strdup_bin failed"); + goto fail; + } + + name = s + seplen; + i++; + } + + if (!(*out_fields[i] = b_strdup(name))) { + DEBUG("b_strdup_bin failed"); + goto fail; + } + + return 1; + +fail: + while (i-- > 0) { + free(*out_fields[i]); + } + return 0; +} + +int tapwin32_parse_tap_spec (char *name, char **out_component_id, char **out_human_name) +{ + char **out_fields[2]; + out_fields[0] = out_component_id; + out_fields[1] = out_human_name; + + return split_spec(name, ":", out_fields, 2); +} + +int tapwin32_parse_tun_spec (char *name, char **out_component_id, char **out_human_name, uint32_t out_addrs[3]) +{ + char *addr_strs[3]; + + char **out_fields[5]; + out_fields[0] = out_component_id; + out_fields[1] = out_human_name; + out_fields[2] = &addr_strs[0]; + out_fields[3] = &addr_strs[1]; + out_fields[4] = &addr_strs[2]; + + if (!split_spec(name, ":", out_fields, 5)) { + goto fail0; + } + + for (int i = 0; i < 3; i++) { + if (!ipaddr_parse_ipv4_addr(addr_strs[i], &out_addrs[i])) { + goto fail1; + } + } + + free(addr_strs[0]); + free(addr_strs[1]); + free(addr_strs[2]); + + return 1; + +fail1: + free(*out_component_id); + free(*out_human_name); + free(addr_strs[0]); + free(addr_strs[1]); + free(addr_strs[2]); +fail0: + return 0; +} + +int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]) +{ + // open adapter key + // used to find all devices with the given ComponentId + HKEY adapter_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &adapter_key) != ERROR_SUCCESS) { + DEBUG("Error opening adapter key"); + return 0; + } + + char net_cfg_instance_id[TAPWIN32_MAX_REG_SIZE]; + int found = 0; + int pres; + + DWORD i; + for (i = 0;; i++) { + DWORD len; + DWORD type; + + char key_name[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(key_name); + if (RegEnumKeyEx(adapter_key, i, key_name, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { + break; + } + + char unit_string[TAPWIN32_MAX_REG_SIZE]; + pres = _snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, key_name); + if (pres < 0 || pres == sizeof(unit_string)) { + continue; + } + HKEY unit_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key) != ERROR_SUCCESS) { + continue; + } + + char component_id[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(component_id); + if (RegQueryValueEx(unit_key, "ComponentId", NULL, &type, (LPBYTE)component_id, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS) + continue; + } + + len = sizeof(net_cfg_instance_id); + if (RegQueryValueEx(unit_key, "NetCfgInstanceId", NULL, &type, (LPBYTE)net_cfg_instance_id, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(unit_key) == ERROR_SUCCESS) + continue; + } + + RegCloseKey(unit_key); + + // check if ComponentId matches + if (!strcmp(component_id, device_component_id)) { + // if no name was given, use the first device with the given ComponentId + if (!device_name) { + found = 1; + break; + } + + // open connection key + char conn_string[TAPWIN32_MAX_REG_SIZE]; + pres = _snprintf(conn_string, sizeof(conn_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, net_cfg_instance_id); + if (pres < 0 || pres == sizeof(conn_string)) { + continue; + } + HKEY conn_key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, conn_string, 0, KEY_READ, &conn_key) != ERROR_SUCCESS) { + continue; + } + + // read name + char name[TAPWIN32_MAX_REG_SIZE]; + len = sizeof(name); + if (RegQueryValueEx(conn_key, "Name", NULL, &type, (LPBYTE)name, &len) != ERROR_SUCCESS || type != REG_SZ) { + ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS) + continue; + } + + ASSERT_FORCE(RegCloseKey(conn_key) == ERROR_SUCCESS) + + // check name + if (!strcmp(name, device_name)) { + found = 1; + break; + } + } + } + + ASSERT_FORCE(RegCloseKey(adapter_key) == ERROR_SUCCESS) + + if (!found) { + return 0; + } + + pres = _snprintf(*device_path, sizeof(*device_path), "\\\\.\\Global\\%s.tap", net_cfg_instance_id); + if (pres < 0 || pres == sizeof(*device_path)) { + return 0; + } + + return 1; +} diff --git a/external/badvpn_dns/tuntap/tapwin32-funcs.h b/external/badvpn_dns/tuntap/tapwin32-funcs.h new file mode 100644 index 00000000..fed66de3 --- /dev/null +++ b/external/badvpn_dns/tuntap/tapwin32-funcs.h @@ -0,0 +1,42 @@ +/** + * @file tapwin32-funcs.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_TUNTAP_TAPWIN32_FUNCS_H +#define BADVPN_TUNTAP_TAPWIN32_FUNCS_H + +#include +#include + +#define TAPWIN32_MAX_REG_SIZE 256 + +int tapwin32_parse_tap_spec (char *name, char **out_component_id, char **out_human_name); +int tapwin32_parse_tun_spec (char *name, char **out_component_id, char **out_human_name, uint32_t out_addrs[3]); +int tapwin32_find_device (char *device_component_id, char *device_name, char (*device_path)[TAPWIN32_MAX_REG_SIZE]); + +#endif diff --git a/external/badvpn_dns/tuntap/wintap-common.h b/external/badvpn_dns/tuntap/wintap-common.h new file mode 100644 index 00000000..922c8512 --- /dev/null +++ b/external/badvpn_dns/tuntap/wintap-common.h @@ -0,0 +1,39 @@ +/** + * @file wintap-common.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @section DESCRIPTION + * + * API definitions for TAP-Win32. + */ + +#define TAP_IOCTL_CONFIG_TUN CTL_CODE(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_GET_MTU CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define TAP_IOCTL_SET_MEDIA_STATUS CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" diff --git a/external/badvpn_dns/udevmonitor/CMakeLists.txt b/external/badvpn_dns/udevmonitor/CMakeLists.txt new file mode 100644 index 00000000..a95f6058 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/CMakeLists.txt @@ -0,0 +1,7 @@ +set(UDEVMONITOR_SOURCES + NCDUdevMonitorParser.c + NCDUdevMonitor.c + NCDUdevCache.c + NCDUdevManager.c +) +badvpn_add_library(udevmonitor "system;flow;stringmap" "" "${UDEVMONITOR_SOURCES}") diff --git a/external/badvpn_dns/udevmonitor/NCDUdevCache.c b/external/badvpn_dns/udevmonitor/NCDUdevCache.c new file mode 100644 index 00000000..cb88821c --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevCache.c @@ -0,0 +1,417 @@ +/** + * @file NCDUdevCache.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +static int string_comparator (void *unused, const char **str1, const char **str2) +{ + int c = strcmp(*str1, *str2); + return B_COMPARE(c, 0); +} + +static void free_device (NCDUdevCache *o, struct NCDUdevCache_device *device) +{ + if (device->is_cleaned) { + // remove from cleaned devices list + LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + } else { + // remove from devices tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + } + + // free map + BStringMap_Free(&device->map); + + // free structure + free(device); +} + +static struct NCDUdevCache_device * lookup_device (NCDUdevCache *o, const char *devpath) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->devices_tree, &devpath); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device; +} + +static void rename_devices (NCDUdevCache *o, const char *prefix, const char *new_prefix) +{ + ASSERT(strlen(prefix) > 0) + + size_t prefix_len = strlen(prefix); + + // lookup prefix + BAVLNode *tree_node = BAVL_Lookup(&o->devices_tree, &prefix); + if (!tree_node) { + return; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // if the result does not begin with prefix, we might gave gotten the device before all + // devices beginning with prefix, so skip it + if (!string_begins_with(device->devpath, prefix)) { + tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + } + + while (tree_node) { + // get next node (must be here because we rename this device) + BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + + // get device + device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // if it doesn't begin with prefix, we're done + if (!string_begins_with(device->devpath, prefix)) { + break; + } + + // build new devpath + char *new_devpath = concat_strings(2, new_prefix, device->devpath + prefix_len); + if (!new_devpath) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_loop0; + } + + // make sure the new name does not exist + if (BAVL_LookupExact(&o->devices_tree, &new_devpath)) { + BLog(BLOG_ERROR, "rename destination already exists"); + goto fail_loop1; + } + + BLog(BLOG_DEBUG, "rename %s -> %s", device->devpath, new_devpath); + + // remove from tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + + // update devpath in map + if (!BStringMap_Set(&device->map, "DEVPATH", new_devpath)) { + BLog(BLOG_ERROR, "BStringMap_Set failed"); + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + goto fail_loop1; + } + + // update devpath pointer + device->devpath = BStringMap_Get(&device->map, "DEVPATH"); + ASSERT(device->devpath) + + // insert to tree + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + + fail_loop1: + free(new_devpath); + fail_loop0: + tree_node = next_tree_node; + } +} + +static int add_device (NCDUdevCache *o, BStringMap map) +{ + ASSERT(BStringMap_Get(&map, "DEVPATH")) + + // alloc structure + struct NCDUdevCache_device *device = malloc(sizeof(*device)); + if (!device) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init map + device->map = map; + + // set device path + device->devpath = BStringMap_Get(&device->map, "DEVPATH"); + + // insert to devices tree + BAVLNode *ex_node; + if (!BAVL_Insert(&o->devices_tree, &device->devices_tree_node, &ex_node)) { + BLog(BLOG_DEBUG, "update %s", device->devpath); + + // get existing device + struct NCDUdevCache_device *ex_device = UPPER_OBJECT(ex_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!ex_device->is_cleaned) + + // remove exiting device + free_device(o, ex_device); + + // insert + ASSERT_EXECUTE(BAVL_Insert(&o->devices_tree, &device->devices_tree_node, NULL)) + } else { + BLog(BLOG_DEBUG, "add %s", device->devpath); + } + + // set not cleaned + device->is_cleaned = 0; + + // set refreshed + device->is_refreshed = 1; + + return 1; + +fail0: + return 0; +} + +void NCDUdevCache_Init (NCDUdevCache *o) +{ + // init devices tree + BAVL_Init(&o->devices_tree, OFFSET_DIFF(struct NCDUdevCache_device, devpath, devices_tree_node), (BAVL_comparator)string_comparator, NULL); + + // init cleaned devices list + LinkedList1_Init(&o->cleaned_devices_list); + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevCache_Free (NCDUdevCache *o) +{ + DebugObject_Free(&o->d_obj); + + // free cleaned devices + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->cleaned_devices_list)) { + struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node); + ASSERT(device->is_cleaned) + free_device(o, device); + } + + // free devices + BAVLNode *tree_node; + while (tree_node = BAVL_GetFirst(&o->devices_tree)) { + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + free_device(o, device); + } +} + +const BStringMap * NCDUdevCache_Query (NCDUdevCache *o, const char *devpath) +{ + DebugObject_Access(&o->d_obj); + + // lookup device + struct NCDUdevCache_device *device = lookup_device(o, devpath); + if (!device) { + return NULL; + } + + // return map + return &device->map; +} + +int NCDUdevCache_Event (NCDUdevCache *o, BStringMap map) +{ + DebugObject_Access(&o->d_obj); + + // get device path + const char *devpath = BStringMap_Get(&map, "DEVPATH"); + if (!devpath) { + BLog(BLOG_ERROR, "missing DEVPATH"); + goto fail; + } + + // get action + const char *action = BStringMap_Get(&map, "ACTION"); + + // if this is a remove event, remove device if we have it + if (action && !strcmp(action, "remove")) { + // remove existing device + struct NCDUdevCache_device *device = lookup_device(o, devpath); + if (device) { + BLog(BLOG_DEBUG, "remove %s", devpath); + free_device(o, device); + } else { + BLog(BLOG_DEBUG, "remove unknown %s", devpath); + } + + // eat map + BStringMap_Free(&map); + + return 1; + } + + // if this is a move event, remove old device and contaned devices + if (action && !strcmp(action, "move")) { + const char *devpath_old = BStringMap_Get(&map, "DEVPATH_OLD"); + if (!devpath_old) { + goto fail_rename0; + } + + // remove old device + struct NCDUdevCache_device *old_device = lookup_device(o, devpath_old); + if (old_device) { + BLog(BLOG_DEBUG, "remove moved %s", old_device->devpath); + free_device(o, old_device); + } + + // construct prefix "/" and new prefix "/" + char *prefix = concat_strings(2, devpath_old, "/"); + if (!prefix) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_rename0;; + } + char *new_prefix = concat_strings(2, devpath, "/"); + if (!new_prefix) { + BLog(BLOG_ERROR, "concat_strings failed"); + goto fail_rename1; + } + + // rename devices with paths starting with prefix + rename_devices(o, prefix, new_prefix); + + free(new_prefix); + fail_rename1: + free(prefix); + fail_rename0:; + } + + // add device + if (!add_device(o, map)) { + BLog(BLOG_DEBUG, "failed to add device %s", devpath); + goto fail; + } + + return 1; + +fail: + return 0; +} + +void NCDUdevCache_StartClean (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + // mark all devices not refreshed + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + while (tree_node) { + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + // set device not refreshed + device->is_refreshed = 0; + + tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + } +} + +void NCDUdevCache_FinishClean (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + // move all devices not marked refreshed to the cleaned devices list + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + while (tree_node) { + BAVLNode *next_tree_node = BAVL_GetNext(&o->devices_tree, tree_node); + + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + if (!device->is_refreshed) { + BLog(BLOG_DEBUG, "clean %s", device->devpath); + + // remove from devices tree + BAVL_Remove(&o->devices_tree, &device->devices_tree_node); + + // insert to cleaned devices list + LinkedList1_Append(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + + // set device cleaned + device->is_cleaned = 1; + } + + tree_node = next_tree_node; + } +} + +int NCDUdevCache_GetCleanedDevice (NCDUdevCache *o, BStringMap *out_map) +{ + DebugObject_Access(&o->d_obj); + + // get cleaned device + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->cleaned_devices_list); + if (!list_node) { + return 0; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(list_node, struct NCDUdevCache_device, cleaned_devices_list_node); + ASSERT(device->is_cleaned) + + // remove from cleaned devices list + LinkedList1_Remove(&o->cleaned_devices_list, &device->cleaned_devices_list_node); + + // give away map + *out_map = device->map; + + // free structure + free(device); + + return 1; +} + +const char * NCDUdevCache_First (NCDUdevCache *o) +{ + DebugObject_Access(&o->d_obj); + + BAVLNode *tree_node = BAVL_GetFirst(&o->devices_tree); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device->devpath; +} + +const char * NCDUdevCache_Next (NCDUdevCache *o, const char *key) +{ + ASSERT(BAVL_LookupExact(&o->devices_tree, &key)) + + BAVLNode *tree_node = BAVL_GetNext(&o->devices_tree, BAVL_LookupExact(&o->devices_tree, &key)); + if (!tree_node) { + return NULL; + } + struct NCDUdevCache_device *device = UPPER_OBJECT(tree_node, struct NCDUdevCache_device, devices_tree_node); + ASSERT(!device->is_cleaned) + + return device->devpath; +} diff --git a/external/badvpn_dns/udevmonitor/NCDUdevCache.h b/external/badvpn_dns/udevmonitor/NCDUdevCache.h new file mode 100644 index 00000000..2d9e8d88 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevCache.h @@ -0,0 +1,66 @@ +/** + * @file NCDUdevCache.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVCACHE_H +#define BADVPN_UDEVMONITOR_NCDUDEVCACHE_H + +#include +#include +#include +#include +#include + +struct NCDUdevCache_device { + BStringMap map; + const char *devpath; + int is_cleaned; + union { + BAVLNode devices_tree_node; + LinkedList1Node cleaned_devices_list_node; + }; + int is_refreshed; +}; + +typedef struct { + BAVL devices_tree; + LinkedList1 cleaned_devices_list; + DebugObject d_obj; +} NCDUdevCache; + +void NCDUdevCache_Init (NCDUdevCache *o); +void NCDUdevCache_Free (NCDUdevCache *o); +const BStringMap * NCDUdevCache_Query (NCDUdevCache *o, const char *devpath); +int NCDUdevCache_Event (NCDUdevCache *o, BStringMap map) WARN_UNUSED; +void NCDUdevCache_StartClean (NCDUdevCache *o); +void NCDUdevCache_FinishClean (NCDUdevCache *o); +int NCDUdevCache_GetCleanedDevice (NCDUdevCache *o, BStringMap *out_map); +const char * NCDUdevCache_First (NCDUdevCache *o); +const char * NCDUdevCache_Next (NCDUdevCache *o, const char *key); + +#endif diff --git a/external/badvpn_dns/udevmonitor/NCDUdevManager.c b/external/badvpn_dns/udevmonitor/NCDUdevManager.c new file mode 100644 index 00000000..4a44fb70 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevManager.c @@ -0,0 +1,547 @@ +/** + * @file NCDUdevManager.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +#include + +#include + +#define RESTART_TIMER_TIME 5000 + +static int event_to_map (NCDUdevMonitor *monitor, BStringMap *out_map); +static void free_event (NCDUdevClient *o, struct NCDUdevClient_event *e); +static void queue_event (NCDUdevManager *o, NCDUdevMonitor *monitor, NCDUdevClient *client); +static void queue_mapless_event (NCDUdevManager *o, const char *devpath, NCDUdevClient *client); +static void process_event (NCDUdevManager *o, NCDUdevMonitor *monitor); +static void try_monitor (NCDUdevManager *o); +static void reset_monitor (NCDUdevManager *o); +static void timer_handler (NCDUdevManager *o); +static void monitor_handler_event (NCDUdevManager *o); +static void monitor_handler_error (NCDUdevManager *o, int is_error); +static void info_monitor_handler_event (NCDUdevManager *o); +static void info_monitor_handler_error (NCDUdevManager *o, int is_error); +static void next_job_handler (NCDUdevClient *o); + +static int event_to_map (NCDUdevMonitor *monitor, BStringMap *out_map) +{ + NCDUdevMonitor_AssertReady(monitor); + + // init map + BStringMap_Init(out_map); + + // insert properties to map + int num_properties = NCDUdevMonitor_GetNumProperties(monitor); + for (int i = 0; i < num_properties; i++) { + const char *name; + const char *value; + NCDUdevMonitor_GetProperty(monitor, i, &name, &value); + + if (!BStringMap_Set(out_map, name, value)) { + BLog(BLOG_ERROR, "BStringMap_Set failed"); + goto fail1; + } + } + + return 1; + +fail1: + BStringMap_Free(out_map); + return 0; +} + +static void free_event (NCDUdevClient *o, struct NCDUdevClient_event *e) +{ + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // free map + if (e->have_map) { + BStringMap_Free(&e->map); + } + + // free devpath + free(e->devpath); + + // free structure + free(e); +} + +static void queue_event (NCDUdevManager *o, NCDUdevMonitor *monitor, NCDUdevClient *client) +{ + NCDUdevMonitor_AssertReady(monitor); + + // alloc event + struct NCDUdevClient_event *e = malloc(sizeof(*e)); + if (!e) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // build map + if (!event_to_map(monitor, &e->map)) { + goto fail1; + } + + // set have map + e->have_map = 1; + + // get devpath + const char *devpath = BStringMap_Get(&e->map, "DEVPATH"); + if (!devpath) { + BLog(BLOG_ERROR, "DEVPATH missing"); + goto fail2; + } + + // copy devpath + if (!(e->devpath = strdup(devpath))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail2; + } + + // insert to client's events list + LinkedList1_Append(&client->events_list, &e->events_list_node); + + // if client is running, set next job + if (client->running) { + BPending_Set(&client->next_job); + } + + return; + +fail2: + BStringMap_Free(&e->map); +fail1: + free(e); +fail0: + return; +} + +static void queue_mapless_event (NCDUdevManager *o, const char *devpath, NCDUdevClient *client) +{ + // alloc event + struct NCDUdevClient_event *e = malloc(sizeof(*e)); + if (!e) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // set have no map + e->have_map = 0; + + // copy devpath + if (!(e->devpath = strdup(devpath))) { + BLog(BLOG_ERROR, "strdup failed"); + goto fail1; + } + + // insert to client's events list + LinkedList1_Append(&client->events_list, &e->events_list_node); + + // if client is running, set next job + if (client->running) { + BPending_Set(&client->next_job); + } + + return; + +fail1: + free(e); +fail0: + return; +} + +static void process_event (NCDUdevManager *o, NCDUdevMonitor *monitor) +{ + NCDUdevMonitor_AssertReady(monitor); + + // build map from event + BStringMap map; + if (!event_to_map(monitor, &map)) { + BLog(BLOG_ERROR, "failed to build map"); + return; + } + + // pass event to cache + if (!NCDUdevCache_Event(&o->cache, map)) { + BLog(BLOG_ERROR, "failed to cache"); + BStringMap_Free(&map); + return; + } + + // queue event to clients + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->clients_list); + while (list_node) { + NCDUdevClient *client = UPPER_OBJECT(list_node, NCDUdevClient, clients_list_node); + queue_event(o, monitor, client); + list_node = LinkedList1Node_Next(list_node); + } +} + +static void try_monitor (NCDUdevManager *o) +{ + ASSERT(!o->have_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + int mode = (o->no_udev ? NCDUDEVMONITOR_MODE_MONITOR_KERNEL : NCDUDEVMONITOR_MODE_MONITOR_UDEV); + + // init monitor + if (!NCDUdevMonitor_Init(&o->monitor, o->reactor, o->manager, mode, o, + (NCDUdevMonitor_handler_event)monitor_handler_event, + (NCDUdevMonitor_handler_error)monitor_handler_error + )) { + BLog(BLOG_ERROR, "NCDUdevMonitor_Init failed"); + + // set restart timer + BReactor_SetTimer(o->reactor, &o->restart_timer); + return; + } + + // set have monitor + o->have_monitor = 1; + + // set not have info monitor + o->have_info_monitor = 0; +} + +static void reset_monitor (NCDUdevManager *o) +{ + ASSERT(o->have_monitor) + ASSERT(!o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + // free monitor + NCDUdevMonitor_Free(&o->monitor); + + // set have no monitor + o->have_monitor = 0; + + // set restart timer + BReactor_SetTimer(o->reactor, &o->restart_timer); +} + +static void timer_handler (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_monitor) + + // try again + try_monitor(o); +} + +static void monitor_handler_event (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(!o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + if (NCDUdevMonitor_IsReadyEvent(&o->monitor)) { + BLog(BLOG_INFO, "monitor ready"); + + // init info monitor + if (!NCDUdevMonitor_Init(&o->info_monitor, o->reactor, o->manager, NCDUDEVMONITOR_MODE_INFO, o, + (NCDUdevMonitor_handler_event)info_monitor_handler_event, + (NCDUdevMonitor_handler_error)info_monitor_handler_error + )) { + BLog(BLOG_ERROR, "NCDUdevMonitor_Init failed"); + reset_monitor(o); + return; + } + + // set have info monitor + o->have_info_monitor = 1; + + // start cache cleanup + NCDUdevCache_StartClean(&o->cache); + + // hold processing monitor events until info monitor is done + return; + } + + // accept event + NCDUdevMonitor_Done(&o->monitor); + + // process event + process_event(o, &o->monitor); +} + +static void monitor_handler_error (NCDUdevManager *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + BLog(BLOG_ERROR, "monitor error"); + + if (o->have_info_monitor) { + // free info monitor + NCDUdevMonitor_Free(&o->info_monitor); + + // set have no info monitor + o->have_info_monitor = 0; + } + + // reset monitor + reset_monitor(o); +} + +static void info_monitor_handler_event (NCDUdevManager *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + // accept event + NCDUdevMonitor_Done(&o->info_monitor); + + // process event + process_event(o, &o->info_monitor); +} + +static void info_monitor_handler_error (NCDUdevManager *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_monitor) + ASSERT(o->have_info_monitor) + ASSERT(!BTimer_IsRunning(&o->restart_timer)) + + if (is_error) { + BLog(BLOG_ERROR, "info monitor error"); + } else { + BLog(BLOG_INFO, "info monitor finished"); + } + + // free info monitor + NCDUdevMonitor_Free(&o->info_monitor); + + // set have no info monitor + o->have_info_monitor = 0; + + if (is_error) { + // reset monitor + reset_monitor(o); + } else { + // continue processing monitor events + NCDUdevMonitor_Done(&o->monitor); + + // finish cache cleanup + NCDUdevCache_FinishClean(&o->cache); + + // collect cleaned devices + BStringMap map; + while (NCDUdevCache_GetCleanedDevice(&o->cache, &map)) { + // get devpath + const char *devpath = BStringMap_Get(&map, "DEVPATH"); + ASSERT(devpath) + + // queue mapless event to clients + LinkedList1Node *list_node = LinkedList1_GetFirst(&o->clients_list); + while (list_node) { + NCDUdevClient *client = UPPER_OBJECT(list_node, NCDUdevClient, clients_list_node); + queue_mapless_event(o, devpath, client); + list_node = LinkedList1Node_Next(list_node); + } + + BStringMap_Free(&map); + } + } +} + +static void next_job_handler (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!LinkedList1_IsEmpty(&o->events_list)) + ASSERT(o->running) + + // get event + struct NCDUdevClient_event *e = UPPER_OBJECT(LinkedList1_GetFirst(&o->events_list), struct NCDUdevClient_event, events_list_node); + + // grab map from event + int have_map = e->have_map; + BStringMap map = e->map; + + // grab devpath from event + char *devpath = e->devpath; + + // remove from events list + LinkedList1_Remove(&o->events_list, &e->events_list_node); + + // free structure + free(e); + + // schedule next event if needed + if (!LinkedList1_IsEmpty(&o->events_list)) { + BPending_Set(&o->next_job); + } + + // give map to handler + o->handler(o->user, devpath, have_map, map); + return; +} + +void NCDUdevManager_Init (NCDUdevManager *o, int no_udev, BReactor *reactor, BProcessManager *manager) +{ + ASSERT(no_udev == 0 || no_udev == 1) + + // init arguments + o->no_udev = no_udev; + o->reactor = reactor; + o->manager = manager; + + // init clients list + LinkedList1_Init(&o->clients_list); + + // init cache + NCDUdevCache_Init(&o->cache); + + // init restart timer + BTimer_Init(&o->restart_timer, RESTART_TIMER_TIME, (BTimer_handler)timer_handler, o); + + // set have no monitor + o->have_monitor = 0; + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevManager_Free (NCDUdevManager *o) +{ + DebugObject_Free(&o->d_obj); + ASSERT(LinkedList1_IsEmpty(&o->clients_list)) + + if (o->have_monitor) { + // free info monitor + if (o->have_info_monitor) { + NCDUdevMonitor_Free(&o->info_monitor); + } + + // free monitor + NCDUdevMonitor_Free(&o->monitor); + } + + // free restart timer + BReactor_RemoveTimer(o->reactor, &o->restart_timer); + + // free cache + NCDUdevCache_Free(&o->cache); +} + +const BStringMap * NCDUdevManager_Query (NCDUdevManager *o, const char *devpath) +{ + DebugObject_Access(&o->d_obj); + + return NCDUdevCache_Query(&o->cache, devpath); +} + +void NCDUdevClient_Init (NCDUdevClient *o, NCDUdevManager *m, void *user, + NCDUdevClient_handler handler) +{ + DebugObject_Access(&m->d_obj); + + // init arguments + o->m = m; + o->user = user; + o->handler = handler; + + // insert to manager's list + LinkedList1_Append(&m->clients_list, &o->clients_list_node); + + // init events list + LinkedList1_Init(&o->events_list); + + // init next job + BPending_Init(&o->next_job, BReactor_PendingGroup(m->reactor), (BPending_handler)next_job_handler, o); + + // set running + o->running = 1; + + // queue all devices from cache + const char *devpath = NCDUdevCache_First(&m->cache); + while (devpath) { + queue_mapless_event(m, devpath, o); + devpath = NCDUdevCache_Next(&m->cache, devpath); + } + + // if this is the first client, init monitor + if (!m->have_monitor && !BTimer_IsRunning(&m->restart_timer)) { + try_monitor(m); + } + + DebugObject_Init(&o->d_obj); +} + +void NCDUdevClient_Free (NCDUdevClient *o) +{ + DebugObject_Free(&o->d_obj); + NCDUdevManager *m = o->m; + + // free events + LinkedList1Node *list_node; + while (list_node = LinkedList1_GetFirst(&o->events_list)) { + struct NCDUdevClient_event *e = UPPER_OBJECT(list_node, struct NCDUdevClient_event, events_list_node); + free_event(o, e); + } + + // free next job + BPending_Free(&o->next_job); + + // remove from manager's list + LinkedList1_Remove(&m->clients_list, &o->clients_list_node); +} + +void NCDUdevClient_Pause (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->running) + + // set not running + o->running = 0; + + // unset next job to avoid reporting queued events + BPending_Unset(&o->next_job); +} + +void NCDUdevClient_Continue (NCDUdevClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->running) + + // set running + o->running = 1; + + // set next job if we have events queued + if (!LinkedList1_IsEmpty(&o->events_list)) { + BPending_Set(&o->next_job); + } +} diff --git a/external/badvpn_dns/udevmonitor/NCDUdevManager.h b/external/badvpn_dns/udevmonitor/NCDUdevManager.h new file mode 100644 index 00000000..26ecfc65 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevManager.h @@ -0,0 +1,84 @@ +/** + * @file NCDUdevManager.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMANAGER_H +#define BADVPN_UDEVMONITOR_NCDUDEVMANAGER_H + +#include +#include +#include +#include +#include +#include + +typedef void (*NCDUdevClient_handler) (void *user, char *devpath, int have_map, BStringMap map); + +typedef struct { + int no_udev; + BReactor *reactor; + BProcessManager *manager; + LinkedList1 clients_list; + NCDUdevCache cache; + BTimer restart_timer; + int have_monitor; + NCDUdevMonitor monitor; + int have_info_monitor; + NCDUdevMonitor info_monitor; + DebugObject d_obj; +} NCDUdevManager; + +typedef struct { + NCDUdevManager *m; + void *user; + NCDUdevClient_handler handler; + LinkedList1Node clients_list_node; + LinkedList1 events_list; + BPending next_job; + int running; + DebugObject d_obj; +} NCDUdevClient; + +struct NCDUdevClient_event { + char *devpath; + int have_map; + BStringMap map; + LinkedList1Node events_list_node; +}; + +void NCDUdevManager_Init (NCDUdevManager *o, int no_udev, BReactor *reactor, BProcessManager *manager); +void NCDUdevManager_Free (NCDUdevManager *o); +const BStringMap * NCDUdevManager_Query (NCDUdevManager *o, const char *devpath); + +void NCDUdevClient_Init (NCDUdevClient *o, NCDUdevManager *m, void *user, + NCDUdevClient_handler handler); +void NCDUdevClient_Free (NCDUdevClient *o); +void NCDUdevClient_Pause (NCDUdevClient *o); +void NCDUdevClient_Continue (NCDUdevClient *o); + +#endif diff --git a/external/badvpn_dns/udevmonitor/NCDUdevMonitor.c b/external/badvpn_dns/udevmonitor/NCDUdevMonitor.c new file mode 100644 index 00000000..56970cd9 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevMonitor.c @@ -0,0 +1,250 @@ +/** + * @file NCDUdevMonitor.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include + +#define PARSER_BUF_SIZE 16384 +#define PARSER_MAX_PROPERTIES 256 + +static void report_error (NCDUdevMonitor *o) +{ + ASSERT(!o->process_running) + ASSERT(!o->input_running) + + DEBUGERROR(&o->d_err, o->handler_error(o->user, (o->process_was_error || o->input_was_error))); +} + +static void process_handler_terminated (NCDUdevMonitor *o, int normally, uint8_t normally_exit_status) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->process_running) + + BLog(BLOG_INFO, "process terminated"); + + // set process not running (so we don't try to kill it) + o->process_running = 0; + + // remember process error + o->process_was_error = !(normally && normally_exit_status == 0); + + if (!o->input_running) { + report_error(o); + return; + } +} + +static void process_handler_closed (NCDUdevMonitor *o, int is_error) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->input_running) + + if (is_error) { + BLog(BLOG_ERROR, "pipe error"); + } else { + BLog(BLOG_INFO, "pipe closed"); + } + + // disconnect connector + StreamRecvConnector_DisconnectInput(&o->connector); + + // set input not running + o->input_running = 0; + + // remember input error + o->input_was_error = is_error; + + if (!o->process_running) { + report_error(o); + return; + } +} + +static void parser_handler (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + + o->handler_event(o->user); + return; +} + +int NCDUdevMonitor_Init (NCDUdevMonitor *o, BReactor *reactor, BProcessManager *manager, int mode, void *user, + NCDUdevMonitor_handler_event handler_event, + NCDUdevMonitor_handler_error handler_error) +{ + ASSERT(mode == NCDUDEVMONITOR_MODE_MONITOR_UDEV || mode == NCDUDEVMONITOR_MODE_INFO || mode == NCDUDEVMONITOR_MODE_MONITOR_KERNEL) + + // init arguments + o->user = user; + o->handler_event = handler_event; + o->handler_error = handler_error; + + // find programs + char *stdbuf_exec = badvpn_find_program("stdbuf"); + char *udevadm_exec = badvpn_find_program("udevadm"); + if (!stdbuf_exec) { + BLog(BLOG_ERROR, "failed to find stdbuf program"); + goto fail0; + } + if (!udevadm_exec) { + BLog(BLOG_ERROR, "failed to find udevadm program"); + goto fail0; + } + + // construct arguments + const char *argv_monitor_udev[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--udev", "--environment", NULL}; + const char *argv_monitor_kernel[] = {stdbuf_exec, "-o", "L", udevadm_exec, "monitor", "--kernel", "--environment", NULL}; + const char *argv_info[] = {stdbuf_exec, "-o", "L", udevadm_exec, "info", "--query", "all", "--export-db", NULL}; + + // choose arguments based on mode + const char **argv = NULL; // to remove warning + switch (mode) { + case NCDUDEVMONITOR_MODE_MONITOR_UDEV: argv = argv_monitor_udev; break; + case NCDUDEVMONITOR_MODE_INFO: argv = argv_info; break; + case NCDUDEVMONITOR_MODE_MONITOR_KERNEL: argv = argv_monitor_kernel; break; + default: ASSERT(0); + } + + // init process + if (!BInputProcess_Init(&o->process, reactor, manager, o, + (BInputProcess_handler_terminated)process_handler_terminated, + (BInputProcess_handler_closed)process_handler_closed + )) { + BLog(BLOG_ERROR, "BInputProcess_Init failed"); + goto fail0; + } + + // init connector + StreamRecvConnector_Init(&o->connector, BReactor_PendingGroup(reactor)); + StreamRecvConnector_ConnectInput(&o->connector, BInputProcess_GetInput(&o->process)); + + // init parser + if (!NCDUdevMonitorParser_Init(&o->parser, StreamRecvConnector_GetOutput(&o->connector), PARSER_BUF_SIZE, PARSER_MAX_PROPERTIES, + (mode == NCDUDEVMONITOR_MODE_INFO), BReactor_PendingGroup(reactor), o, + (NCDUdevMonitorParser_handler)parser_handler + )) { + BLog(BLOG_ERROR, "NCDUdevMonitorParser_Init failed"); + goto fail1; + } + + // start process + if (!BInputProcess_Start(&o->process, stdbuf_exec, (char **)argv, NULL)) { + BLog(BLOG_ERROR, "BInputProcess_Start failed"); + goto fail2; + } + + // set process running, input running + o->process_running = 1; + o->input_running = 1; + + free(udevadm_exec); + free(stdbuf_exec); + + DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor)); + DebugObject_Init(&o->d_obj); + return 1; + +fail2: + NCDUdevMonitorParser_Free(&o->parser); +fail1: + StreamRecvConnector_Free(&o->connector); + BInputProcess_Free(&o->process); +fail0: + free(udevadm_exec); + free(stdbuf_exec); + return 0; +} + +void NCDUdevMonitor_Free (NCDUdevMonitor *o) +{ + DebugObject_Free(&o->d_obj); + DebugError_Free(&o->d_err); + + // free parser + NCDUdevMonitorParser_Free(&o->parser); + + // free connector + StreamRecvConnector_Free(&o->connector); + + // kill process it it's running + if (o->process_running) { + BInputProcess_Kill(&o->process); + } + + // free process + BInputProcess_Free(&o->process); +} + +void NCDUdevMonitor_Done (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + NCDUdevMonitorParser_Done(&o->parser); +} + +int NCDUdevMonitor_IsReadyEvent (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + return NCDUdevMonitorParser_IsReadyEvent(&o->parser); +} +void NCDUdevMonitor_AssertReady (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); +} + +int NCDUdevMonitor_GetNumProperties (NCDUdevMonitor *o) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + return NCDUdevMonitorParser_GetNumProperties(&o->parser); +} + +void NCDUdevMonitor_GetProperty (NCDUdevMonitor *o, int index, const char **name, const char **value) +{ + DebugObject_Access(&o->d_obj); + DebugError_AssertNoError(&o->d_err); + NCDUdevMonitorParser_AssertReady(&o->parser); + + NCDUdevMonitorParser_GetProperty(&o->parser, index, name, value); +} diff --git a/external/badvpn_dns/udevmonitor/NCDUdevMonitor.h b/external/badvpn_dns/udevmonitor/NCDUdevMonitor.h new file mode 100644 index 00000000..eae5747d --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevMonitor.h @@ -0,0 +1,71 @@ +/** + * @file NCDUdevMonitor.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMONITOR_H +#define BADVPN_UDEVMONITOR_NCDUDEVMONITOR_H + +#include +#include +#include +#include +#include + +#define NCDUDEVMONITOR_MODE_MONITOR_UDEV 0 +#define NCDUDEVMONITOR_MODE_INFO 1 +#define NCDUDEVMONITOR_MODE_MONITOR_KERNEL 2 + +typedef void (*NCDUdevMonitor_handler_event) (void *user); +typedef void (*NCDUdevMonitor_handler_error) (void *user, int is_error); + +typedef struct { + void *user; + NCDUdevMonitor_handler_event handler_event; + NCDUdevMonitor_handler_error handler_error; + BInputProcess process; + int process_running; + int process_was_error; + int input_running; + int input_was_error; + StreamRecvConnector connector; + NCDUdevMonitorParser parser; + DebugObject d_obj; + DebugError d_err; +} NCDUdevMonitor; + +int NCDUdevMonitor_Init (NCDUdevMonitor *o, BReactor *reactor, BProcessManager *manager, int mode, void *user, + NCDUdevMonitor_handler_event handler_event, + NCDUdevMonitor_handler_error handler_error) WARN_UNUSED; +void NCDUdevMonitor_Free (NCDUdevMonitor *o); +void NCDUdevMonitor_Done (NCDUdevMonitor *o); +void NCDUdevMonitor_AssertReady (NCDUdevMonitor *o); +int NCDUdevMonitor_IsReadyEvent (NCDUdevMonitor *o); +int NCDUdevMonitor_GetNumProperties (NCDUdevMonitor *o); +void NCDUdevMonitor_GetProperty (NCDUdevMonitor *o, int index, const char **name, const char **value); + +#endif diff --git a/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.c b/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.c new file mode 100644 index 00000000..9ec0ef13 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.c @@ -0,0 +1,358 @@ +/** + * @file NCDUdevMonitorParser.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#define PROPERTY_REGEX "^([^=]+)=(.*)$" + +static uint8_t * find_end (uint8_t *buf, size_t len) +{ + while (len >= 2) { + if (buf[0] == '\n' && buf[1] == '\n') { + return (buf + 2); + } + buf++; + len--; + } + + return NULL; +} + +static int parse_property (NCDUdevMonitorParser *o, char *data) +{ + ASSERT(o->ready_num_properties >= 0) + ASSERT(o->ready_num_properties <= o->max_properties) + + if (o->ready_num_properties == o->max_properties) { + BLog(BLOG_ERROR, "too many properties"); + return 0; + } + struct NCDUdevMonitorParser_property *prop = &o->ready_properties[o->ready_num_properties]; + + // execute property regex + regmatch_t matches[3]; + if (regexec(&o->property_regex, data, 3, matches, 0) != 0) { + BLog(BLOG_ERROR, "failed to parse property"); + return 0; + } + + // extract components + prop->name = data + matches[1].rm_so; + *(data + matches[1].rm_eo) = '\0'; + prop->value = data + matches[2].rm_so; + *(data + matches[2].rm_eo) = '\0'; + + // register property + o->ready_num_properties++; + + return 1; +} + +static int parse_message (NCDUdevMonitorParser *o) +{ + ASSERT(!o->is_ready) + ASSERT(o->ready_len >= 2) + ASSERT(o->buf[o->ready_len - 2] == '\n') + ASSERT(o->buf[o->ready_len - 1] == '\n') + + // zero terminate message (replacing the second newline) + o->buf[o->ready_len - 1] = '\0'; + + // start parsing + char *line = (char *)o->buf; + int first_line = 1; + + // set is not ready event + o->ready_is_ready_event = 0; + + // init properties + o->ready_num_properties = 0; + + do { + // find end of line + char *line_end = strchr(line, '\n'); + ASSERT(line_end) + + // zero terminate line + *line_end = '\0'; + + if (o->is_info_mode) { + // ignore W: entries with missing space + if (string_begins_with(line, "W:")) { + goto nextline; + } + + // parse prefix + if (strlen(line) < 3 || line[1] != ':' || line[2] != ' ') { + BLog(BLOG_ERROR, "failed to parse head"); + return 0; + } + char line_type = line[0]; + char *line_value = line + 3; + + if (first_line) { + if (line_type != 'P') { + BLog(BLOG_ERROR, "wrong first line type"); + return 0; + } + } else { + if (line_type == 'E') { + if (!parse_property(o, line_value)) { + return 0; + } + } + } + } else { + if (first_line) { + // is this the initial informational message? + if (string_begins_with(line, "monitor")) { + o->ready_is_ready_event = 1; + break; + } + + // check first line + if (!string_begins_with(line, "UDEV ") && !string_begins_with(line, "KERNEL")) { + BLog(BLOG_ERROR, "failed to parse head"); + return 0; + } + } else { + if (!parse_property(o, line)) { + return 0; + } + } + } + nextline: + + first_line = 0; + line = line_end + 1; + } while (*line); + + // set ready + o->is_ready = 1; + + return 1; +} + +static void process_data (NCDUdevMonitorParser *o) +{ + ASSERT(!o->is_ready) + + while (1) { + // look for end of event + uint8_t *c = find_end(o->buf, o->buf_used); + if (!c) { + // check for out of buffer condition + if (o->buf_used == o->buf_size) { + BLog(BLOG_ERROR, "out of buffer"); + o->buf_used = 0; + } + + // receive more data + StreamRecvInterface_Receiver_Recv(o->input, o->buf + o->buf_used, o->buf_size - o->buf_used); + return; + } + + // remember message length + o->ready_len = c - o->buf; + + // parse message + if (parse_message(o)) { + break; + } + + // shift buffer + memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len); + o->buf_used -= o->ready_len; + } + + // call handler + o->handler(o->user); + return; +} + +static void input_handler_done (NCDUdevMonitorParser *o, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->is_ready) + ASSERT(data_len > 0) + ASSERT(data_len <= o->buf_size - o->buf_used) + + // increment buffer position + o->buf_used += data_len; + + // process data + process_data(o); + return; +} + +static void done_job_handler (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + // shift buffer + memmove(o->buf, o->buf + o->ready_len, o->buf_used - o->ready_len); + o->buf_used -= o->ready_len; + + // set not ready + o->is_ready = 0; + + // process data + process_data(o); + return; +} + +int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties, + int is_info_mode, BPendingGroup *pg, void *user, + NCDUdevMonitorParser_handler handler) +{ + ASSERT(buf_size > 0) + ASSERT(max_properties >= 0) + ASSERT(is_info_mode == 0 || is_info_mode == 1) + + // init arguments + o->input = input; + o->buf_size = buf_size; + o->max_properties = max_properties; + o->is_info_mode = is_info_mode; + o->user = user; + o->handler = handler; + + // init input + StreamRecvInterface_Receiver_Init(o->input, (StreamRecvInterface_handler_done)input_handler_done, o); + + // init property regex + if (regcomp(&o->property_regex, PROPERTY_REGEX, REG_EXTENDED) != 0) { + BLog(BLOG_ERROR, "regcomp failed"); + goto fail1; + } + + // init done job + BPending_Init(&o->done_job, pg, (BPending_handler)done_job_handler, o); + + // allocate buffer + if (!(o->buf = malloc(o->buf_size))) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail2; + } + + // set buffer position + o->buf_used = 0; + + // set not ready + o->is_ready = 0; + + // allocate properties + if (!(o->ready_properties = BAllocArray(o->max_properties, sizeof(o->ready_properties[0])))) { + BLog(BLOG_ERROR, "BAllocArray failed"); + goto fail3; + } + + // start receiving + StreamRecvInterface_Receiver_Recv(o->input, o->buf, o->buf_size); + + DebugObject_Init(&o->d_obj); + return 1; + +fail3: + free(o->buf); +fail2: + BPending_Free(&o->done_job); + regfree(&o->property_regex); +fail1: + return 0; +} + +void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o) +{ + DebugObject_Free(&o->d_obj); + + // free properties + BFree(o->ready_properties); + + // free buffer + free(o->buf); + + // free done job + BPending_Free(&o->done_job); + + // free property regex + regfree(&o->property_regex); +} + +void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) +} + +void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + // schedule done job + BPending_Set(&o->done_job); +} + +int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + return o->ready_is_ready_event; +} + +int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + + return o->ready_num_properties; +} + +void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->is_ready) + ASSERT(index >= 0) + ASSERT(index < o->ready_num_properties) + + *name = o->ready_properties[index].name; + *value = o->ready_properties[index].value; +} diff --git a/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.h b/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.h new file mode 100644 index 00000000..910bbe03 --- /dev/null +++ b/external/badvpn_dns/udevmonitor/NCDUdevMonitorParser.h @@ -0,0 +1,76 @@ +/** + * @file NCDUdevMonitorParser.h + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDEVMONITOR_NCDUDEVMONITORPARSER_H +#define BADVPN_UDEVMONITOR_NCDUDEVMONITORPARSER_H + +#include +#include + +#include +#include +#include + +typedef void (*NCDUdevMonitorParser_handler) (void *user); + +struct NCDUdevMonitorParser_property { + char *name; + char *value; +}; + +typedef struct { + StreamRecvInterface *input; + int buf_size; + int max_properties; + int is_info_mode; + void *user; + NCDUdevMonitorParser_handler handler; + regex_t property_regex; + BPending done_job; + uint8_t *buf; + int buf_used; + int is_ready; + int ready_len; + int ready_is_ready_event; + struct NCDUdevMonitorParser_property *ready_properties; + int ready_num_properties; + DebugObject d_obj; +} NCDUdevMonitorParser; + +int NCDUdevMonitorParser_Init (NCDUdevMonitorParser *o, StreamRecvInterface *input, int buf_size, int max_properties, + int is_info_mode, BPendingGroup *pg, void *user, + NCDUdevMonitorParser_handler handler) WARN_UNUSED; +void NCDUdevMonitorParser_Free (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_AssertReady (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_Done (NCDUdevMonitorParser *o); +int NCDUdevMonitorParser_IsReadyEvent (NCDUdevMonitorParser *o); +int NCDUdevMonitorParser_GetNumProperties (NCDUdevMonitorParser *o); +void NCDUdevMonitorParser_GetProperty (NCDUdevMonitorParser *o, int index, const char **name, const char **value); + +#endif diff --git a/external/badvpn_dns/udpgw/CMakeLists.txt b/external/badvpn_dns/udpgw/CMakeLists.txt new file mode 100644 index 00000000..c8c798c1 --- /dev/null +++ b/external/badvpn_dns/udpgw/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(badvpn-udpgw + udpgw.c +) +target_link_libraries(badvpn-udpgw system flow flowextra) + +install( + TARGETS badvpn-udpgw + RUNTIME DESTINATION bin +) diff --git a/external/badvpn_dns/udpgw/udpgw.c b/external/badvpn_dns/udpgw/udpgw.c new file mode 100644 index 00000000..9c6a341b --- /dev/null +++ b/external/badvpn_dns/udpgw/udpgw.c @@ -0,0 +1,1473 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BADVPN_USE_WINAPI +#include +#include +#include +#endif + +#include + +#include + +#define LOGGER_STDOUT 1 +#define LOGGER_SYSLOG 2 + +#define DNS_UPDATE_TIME 2000 + +struct client { + BConnection con; + BAddr addr; + BTimer disconnect_timer; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + PacketPassFairQueue send_queue; + PacketStreamSender send_sender; + BAVL connections_tree; + LinkedList1 connections_list; + int num_connections; + LinkedList1 closing_connections_list; + LinkedList1Node clients_list_node; +}; + +struct connection { + struct client *client; + uint16_t conid; + BAddr addr; + BAddr orig_addr; + const uint8_t *first_data; + int first_data_len; + btime_t last_use_time; + int closing; + BPending first_job; + BufferWriter *send_if; + PacketProtoFlow send_ppflow; + PacketPassFairQueueFlow send_qflow; + union { + struct { + BDatagram udp_dgram; + int local_port_index; + BufferWriter udp_send_writer; + PacketBuffer udp_send_buffer; + SinglePacketBuffer udp_recv_buffer; + PacketPassInterface udp_recv_if; + BAVLNode connections_tree_node; + LinkedList1Node connections_list_node; + }; + struct { + LinkedList1Node closing_connections_list_node; + }; + }; +}; + +// command-line options +struct { + int help; + int version; + int logger; + #ifndef BADVPN_USE_WINAPI + char *logger_syslog_facility; + char *logger_syslog_ident; + #endif + int loglevel; + int loglevels[BLOG_NUM_CHANNELS]; + char *listen_addrs[MAX_LISTEN_ADDRS]; + int num_listen_addrs; + int udp_mtu; + int max_clients; + int max_connections_for_client; + int client_socket_sndbuf; + int local_udp_num_ports; + char *local_udp_addr; + int local_udp_ip6_num_ports; + char *local_udp_ip6_addr; + int unique_local_ports; +} options; + +// MTUs +int udpgw_mtu; +int pp_mtu; + +// listen addresses +BAddr listen_addrs[MAX_LISTEN_ADDRS]; +int num_listen_addrs; + +// local UDP port range, if options.local_udp_num_ports>=0 +BAddr local_udp_addr; + +// local UDP/IPv6 port range, if options.local_udp_ip6_num_ports>=0 +BAddr local_udp_ip6_addr; + +// DNS forwarding +BAddr dns_addr; +btime_t last_dns_update_time; + +// reactor +BReactor ss; + +// listeners +BListener listeners[MAX_LISTEN_ADDRS]; +int num_listeners; + +// clients +LinkedList1 clients_list; +int num_clients; + +static void print_help (const char *name); +static void print_version (void); +static int parse_arguments (int argc, char *argv[]); +static int process_arguments (void); +static void signal_handler (void *unused); +static void listener_handler (BListener *listener); +static void client_free (struct client *client); +static void client_logfunc (struct client *client); +static void client_log (struct client *client, int level, const char *fmt, ...); +static void client_disconnect_timer_handler (struct client *client); +static void client_connection_handler (struct client *client, int event); +static void client_decoder_handler_error (struct client *client); +static void client_recv_if_handler_send (struct client *client, uint8_t *data, int data_len); +static int get_local_num_ports (int addr_type); +static BAddr get_local_addr (int addr_type); +static uint8_t * build_port_usage_array_and_find_least_used_connection (BAddr remote_addr, struct connection **out_con); +static void connection_init (struct client *client, uint16_t conid, BAddr addr, BAddr orig_addr, const uint8_t *data, int data_len); +static void connection_free (struct connection *con); +static void connection_logfunc (struct connection *con); +static void connection_log (struct connection *con, int level, const char *fmt, ...); +static void connection_free_udp (struct connection *con); +static void connection_first_job_handler (struct connection *con); +static void connection_send_to_client (struct connection *con, uint8_t flags, const uint8_t *data, int data_len); +static int connection_send_to_udp (struct connection *con, const uint8_t *data, int data_len); +static void connection_close (struct connection *con); +static void connection_send_qflow_busy_handler (struct connection *con); +static void connection_dgram_handler_event (struct connection *con, int event); +static void connection_udp_recv_if_handler_send (struct connection *con, uint8_t *data, int data_len); +static struct connection * find_connection (struct client *client, uint16_t conid); +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); +static void maybe_update_dns (void); + +int main (int argc, char **argv) +{ + if (argc <= 0) { + return 1; + } + + // open standard streams + open_standard_streams(); + + // parse command-line arguments + if (!parse_arguments(argc, argv)) { + fprintf(stderr, "Failed to parse arguments\n"); + print_help(argv[0]); + goto fail0; + } + + // handle --help and --version + if (options.help) { + print_version(); + print_help(argv[0]); + return 0; + } + if (options.version) { + print_version(); + return 0; + } + + // initialize logger + switch (options.logger) { + case LOGGER_STDOUT: + BLog_InitStdout(); + break; + #ifndef BADVPN_USE_WINAPI + case LOGGER_SYSLOG: + if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) { + fprintf(stderr, "Failed to initialize syslog logger\n"); + goto fail0; + } + break; + #endif + default: + ASSERT(0); + } + + // configure logger channels + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + if (options.loglevels[i] >= 0) { + BLog_SetChannelLoglevel(i, options.loglevels[i]); + } + else if (options.loglevel >= 0) { + BLog_SetChannelLoglevel(i, options.loglevel); + } + } + + BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION); + + // initialize network + if (!BNetwork_GlobalInit()) { + BLog(BLOG_ERROR, "BNetwork_GlobalInit failed"); + goto fail1; + } + + // process arguments + if (!process_arguments()) { + BLog(BLOG_ERROR, "Failed to process arguments"); + goto fail1; + } + + // compute MTUs + udpgw_mtu = udpgw_compute_mtu(options.udp_mtu); + if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { + udpgw_mtu = PACKETPROTO_MAXPAYLOAD; + } + pp_mtu = udpgw_mtu + sizeof(struct packetproto_header); + + // init time + BTime_Init(); + + // init DNS forwarding + BAddr_InitNone(&dns_addr); + last_dns_update_time = INT64_MIN; + maybe_update_dns(); + + // init reactor + if (!BReactor_Init(&ss)) { + BLog(BLOG_ERROR, "BReactor_Init failed"); + goto fail1; + } + + // setup signal handler + if (!BSignal_Init(&ss, signal_handler, NULL)) { + BLog(BLOG_ERROR, "BSignal_Init failed"); + goto fail2; + } + + // initialize listeners + num_listeners = 0; + while (num_listeners < num_listen_addrs) { + if (!BListener_Init(&listeners[num_listeners], listen_addrs[num_listeners], &ss, &listeners[num_listeners], (BListener_handler)listener_handler)) { + BLog(BLOG_ERROR, "Listener_Init failed"); + goto fail3; + } + num_listeners++; + } + + // init clients list + LinkedList1_Init(&clients_list); + num_clients = 0; + + // enter event loop + BLog(BLOG_NOTICE, "entering event loop"); + BReactor_Exec(&ss); + + // free clients + while (!LinkedList1_IsEmpty(&clients_list)) { + struct client *client = UPPER_OBJECT(LinkedList1_GetFirst(&clients_list), struct client, clients_list_node); + client_free(client); + } +fail3: + // free listeners + while (num_listeners > 0) { + num_listeners--; + BListener_Free(&listeners[num_listeners]); + } + // finish signal handling + BSignal_Finish(); +fail2: + // free reactor + BReactor_Free(&ss); +fail1: + // free logger + BLog(BLOG_NOTICE, "exiting"); + BLog_Free(); +fail0: + // finish debug objects + DebugObjectGlobal_Finish(); + + return 1; +} + +void print_help (const char *name) +{ + printf( + "Usage:\n" + " %s\n" + " [--help]\n" + " [--version]\n" + " [--logger <"LOGGERS_STRING">]\n" + #ifndef BADVPN_USE_WINAPI + " (logger=syslog?\n" + " [--syslog-facility ]\n" + " [--syslog-ident ]\n" + " )\n" + #endif + " [--loglevel <0-5/none/error/warning/notice/info/debug>]\n" + " [--channel-loglevel <0-5/none/error/warning/notice/info/debug>] ...\n" + " [--listen-addr ] ...\n" + " [--udp-mtu ]\n" + " [--max-clients ]\n" + " [--max-connections-for-client ]\n" + " [--client-socket-sndbuf ]\n" + " [--local-udp-addrs ]\n" + " [--local-udp-ip6-addrs ]\n" + " [--unique-local-ports]\n" + "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", + name + ); +} + +void print_version (void) +{ + printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n"); +} + +int parse_arguments (int argc, char *argv[]) +{ + if (argc <= 0) { + return 0; + } + + options.help = 0; + options.version = 0; + options.logger = LOGGER_STDOUT; + #ifndef BADVPN_USE_WINAPI + options.logger_syslog_facility = "daemon"; + options.logger_syslog_ident = argv[0]; + #endif + options.loglevel = -1; + for (int i = 0; i < BLOG_NUM_CHANNELS; i++) { + options.loglevels[i] = -1; + } + options.num_listen_addrs = 0; + options.udp_mtu = DEFAULT_UDP_MTU; + options.max_clients = DEFAULT_MAX_CLIENTS; + options.max_connections_for_client = DEFAULT_MAX_CONNECTIONS_FOR_CLIENT; + options.client_socket_sndbuf = CLIENT_DEFAULT_SOCKET_SEND_BUFFER; + options.local_udp_num_ports = -1; + options.local_udp_ip6_num_ports = -1; + options.unique_local_ports = 0; + + int i; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strcmp(arg, "--help")) { + options.help = 1; + } + else if (!strcmp(arg, "--version")) { + options.version = 1; + } + else if (!strcmp(arg, "--logger")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + char *arg2 = argv[i + 1]; + if (!strcmp(arg2, "stdout")) { + options.logger = LOGGER_STDOUT; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg2, "syslog")) { + options.logger = LOGGER_SYSLOG; + } + #endif + else { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + #ifndef BADVPN_USE_WINAPI + else if (!strcmp(arg, "--syslog-facility")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_facility = argv[i + 1]; + i++; + } + else if (!strcmp(arg, "--syslog-ident")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + options.logger_syslog_ident = argv[i + 1]; + i++; + } + #endif + else if (!strcmp(arg, "--loglevel")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--channel-loglevel")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + int channel = BLogGlobal_GetChannelByName(argv[i + 1]); + if (channel < 0) { + fprintf(stderr, "%s: wrong channel argument\n", arg); + return 0; + } + int loglevel = parse_loglevel(argv[i + 2]); + if (loglevel < 0) { + fprintf(stderr, "%s: wrong loglevel argument\n", arg); + return 0; + } + options.loglevels[channel] = loglevel; + i += 2; + } + else if (!strcmp(arg, "--listen-addr")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if (options.num_listen_addrs == MAX_LISTEN_ADDRS) { + fprintf(stderr, "%s: too many\n", arg); + return 0; + } + options.listen_addrs[options.num_listen_addrs] = argv[i + 1]; + options.num_listen_addrs++; + i++; + } + else if (!strcmp(arg, "--udp-mtu")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.udp_mtu = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-clients")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_clients = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--max-connections-for-client")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.max_connections_for_client = atoi(argv[i + 1])) <= 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--client-socket-sndbuf")) { + if (1 >= argc - i) { + fprintf(stderr, "%s: requires an argument\n", arg); + return 0; + } + if ((options.client_socket_sndbuf = atoi(argv[i + 1])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i++; + } + else if (!strcmp(arg, "--local-udp-addrs")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + options.local_udp_addr = argv[i + 1]; + if ((options.local_udp_num_ports = atoi(argv[i + 2])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i += 2; + } + else if (!strcmp(arg, "--local-udp-ip6-addrs")) { + if (2 >= argc - i) { + fprintf(stderr, "%s: requires two arguments\n", arg); + return 0; + } + options.local_udp_ip6_addr = argv[i + 1]; + if ((options.local_udp_ip6_num_ports = atoi(argv[i + 2])) < 0) { + fprintf(stderr, "%s: wrong argument\n", arg); + return 0; + } + i += 2; + } + else if (!strcmp(arg, "--unique-local-ports")) { + options.unique_local_ports = 1; + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 0; + } + } + + if (options.help || options.version) { + return 1; + } + + return 1; +} + +int process_arguments (void) +{ + // resolve listen addresses + num_listen_addrs = 0; + while (num_listen_addrs < options.num_listen_addrs) { + if (!BAddr_Parse(&listen_addrs[num_listen_addrs], options.listen_addrs[num_listen_addrs], NULL, 0)) { + BLog(BLOG_ERROR, "listen addr: BAddr_Parse failed"); + return 0; + } + num_listen_addrs++; + } + + // resolve local UDP address + if (options.local_udp_num_ports >= 0) { + if (!BAddr_Parse(&local_udp_addr, options.local_udp_addr, NULL, 0)) { + BLog(BLOG_ERROR, "local udp addr: BAddr_Parse failed"); + return 0; + } + if (local_udp_addr.type != BADDR_TYPE_IPV4) { + BLog(BLOG_ERROR, "local udp addr: must be an IPv4 address"); + return 0; + } + } + + // resolve local UDP/IPv6 address + if (options.local_udp_ip6_num_ports >= 0) { + if (!BAddr_Parse(&local_udp_ip6_addr, options.local_udp_ip6_addr, NULL, 0)) { + BLog(BLOG_ERROR, "local udp ip6 addr: BAddr_Parse failed"); + return 0; + } + if (local_udp_ip6_addr.type != BADDR_TYPE_IPV6) { + BLog(BLOG_ERROR, "local udp ip6 addr: must be an IPv6 address"); + return 0; + } + } + + return 1; +} + +void signal_handler (void *unused) +{ + BLog(BLOG_NOTICE, "termination requested"); + + // exit event loop + BReactor_Quit(&ss, 1); +} + +void listener_handler (BListener *listener) +{ + if (num_clients == options.max_clients) { + BLog(BLOG_ERROR, "maximum number of clients reached"); + goto fail0; + } + + // allocate structure + struct client *client = (struct client *)malloc(sizeof(*client)); + if (!client) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // accept client + if (!BConnection_Init(&client->con, BConnection_source_listener(listener, &client->addr), &ss, client, (BConnection_handler)client_connection_handler)) { + BLog(BLOG_ERROR, "BConnection_Init failed"); + goto fail1; + } + + // limit socket send buffer, else our scheduling is pointless + if (options.client_socket_sndbuf > 0) { + if (!BConnection_SetSendBuffer(&client->con, options.client_socket_sndbuf)) { + BLog(BLOG_WARNING, "BConnection_SetSendBuffer failed"); + } + } + + // init connection interfaces + BConnection_SendAsync_Init(&client->con); + BConnection_RecvAsync_Init(&client->con); + + // init disconnect timer + BTimer_Init(&client->disconnect_timer, CLIENT_DISCONNECT_TIMEOUT, (BTimer_handler)client_disconnect_timer_handler, client); + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // init recv interface + PacketPassInterface_Init(&client->recv_if, udpgw_mtu, (PacketPassInterface_handler_send)client_recv_if_handler_send, client, BReactor_PendingGroup(&ss)); + + // init recv decoder + if (!PacketProtoDecoder_Init(&client->recv_decoder, BConnection_RecvAsync_GetIf(&client->con), &client->recv_if, BReactor_PendingGroup(&ss), client, + (PacketProtoDecoder_handler_error)client_decoder_handler_error + )) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail2; + } + + // init send sender + PacketStreamSender_Init(&client->send_sender, BConnection_SendAsync_GetIf(&client->con), pp_mtu, BReactor_PendingGroup(&ss)); + + // init send queue + if (!PacketPassFairQueue_Init(&client->send_queue, PacketStreamSender_GetInput(&client->send_sender), BReactor_PendingGroup(&ss), 0, 1)) { + BLog(BLOG_ERROR, "PacketPassFairQueue_Init failed"); + goto fail3; + } + + // init connections tree + BAVL_Init(&client->connections_tree, OFFSET_DIFF(struct connection, conid, connections_tree_node), (BAVL_comparator)uint16_comparator, NULL); + + // init connections list + LinkedList1_Init(&client->connections_list); + + // set zero connections + client->num_connections = 0; + + // init closing connections list + LinkedList1_Init(&client->closing_connections_list); + + // insert to clients list + LinkedList1_Append(&clients_list, &client->clients_list_node); + num_clients++; + + client_log(client, BLOG_INFO, "connected"); + + return; + +fail3: + PacketStreamSender_Free(&client->send_sender); + PacketProtoDecoder_Free(&client->recv_decoder); +fail2: + PacketPassInterface_Free(&client->recv_if); + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + BConnection_Free(&client->con); +fail1: + free(client); +fail0: + return; +} + +void client_free (struct client *client) +{ + // allow freeing send queue flows + PacketPassFairQueue_PrepareFree(&client->send_queue); + + // free connections + while (!LinkedList1_IsEmpty(&client->connections_list)) { + struct connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&client->connections_list), struct connection, connections_list_node); + connection_free(con); + } + + // free closing connections + while (!LinkedList1_IsEmpty(&client->closing_connections_list)) { + struct connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&client->closing_connections_list), struct connection, closing_connections_list_node); + connection_free(con); + } + + // remove from clients list + LinkedList1_Remove(&clients_list, &client->clients_list_node); + num_clients--; + + // free send queue + PacketPassFairQueue_Free(&client->send_queue); + + // free send sender + PacketStreamSender_Free(&client->send_sender); + + // free recv decoder + PacketProtoDecoder_Free(&client->recv_decoder); + + // free recv interface + PacketPassInterface_Free(&client->recv_if); + + // free disconnect timer + BReactor_RemoveTimer(&ss, &client->disconnect_timer); + + // free connection interfaces + BConnection_RecvAsync_Free(&client->con); + BConnection_SendAsync_Free(&client->con); + + // free connection + BConnection_Free(&client->con); + + // free structure + free(client); +} + +void client_logfunc (struct client *client) +{ + char addr[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&client->addr, addr); + + BLog_Append("client (%s): ", addr); +} + +void client_log (struct client *client, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void client_disconnect_timer_handler (struct client *client) +{ + client_log(client, BLOG_INFO, "timed out, disconnecting"); + + // free client + client_free(client); +} + +void client_connection_handler (struct client *client, int event) +{ + if (event == BCONNECTION_EVENT_RECVCLOSED) { + client_log(client, BLOG_INFO, "client closed"); + } else { + client_log(client, BLOG_INFO, "client error"); + } + + // free client + client_free(client); +} + +void client_decoder_handler_error (struct client *client) +{ + client_log(client, BLOG_ERROR, "decoder error"); + + // free client + client_free(client); +} + +void client_recv_if_handler_send (struct client *client, uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&client->recv_if); + + // parse header + if (data_len < sizeof(struct udpgw_header)) { + client_log(client, BLOG_ERROR, "missing header"); + return; + } + struct udpgw_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + uint16_t conid = ltoh16(header.conid); + + // reset disconnect timer + BReactor_SetTimer(&ss, &client->disconnect_timer); + + // if this is keepalive, ignore any payload + if ((flags & UDPGW_CLIENT_FLAG_KEEPALIVE)) { + client_log(client, BLOG_DEBUG, "received keepalive"); + return; + } + + // parse address + BAddr orig_addr; + if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + client_log(client, BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&orig_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + client_log(client, BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&orig_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check payload length + if (data_len > options.udp_mtu) { + client_log(client, BLOG_ERROR, "too much data"); + return; + } + + // find connection + struct connection *con = find_connection(client, conid); + ASSERT(!con || !con->closing) + + // if connection exists, close it if needed + if (con && ((flags & UDPGW_CLIENT_FLAG_REBIND) || !BAddr_Compare(&con->orig_addr, &orig_addr))) { + connection_log(con, BLOG_DEBUG, "close old"); + connection_close(con); + con = NULL; + } + + // if connection doesn't exists, create it + if (!con) { + // check number of connections + if (client->num_connections == options.max_connections_for_client) { + // close least recently used connection + con = UPPER_OBJECT(LinkedList1_GetFirst(&client->connections_list), struct connection, connections_list_node); + connection_close(con); + } + + // if this is DNS, replace actual address, but keep still remember the orig_addr + BAddr addr = orig_addr; + if ((flags & UDPGW_CLIENT_FLAG_DNS)) { + maybe_update_dns(); + if (dns_addr.type == BADDR_TYPE_NONE) { + client_log(client, BLOG_WARNING, "received DNS packet, but no DNS server available"); + } else { + client_log(client, BLOG_DEBUG, "received DNS"); + addr = dns_addr; + } + } + + // create new connection + connection_init(client, conid, addr, orig_addr, data, data_len); + } else { + // submit packet to existing connection + connection_send_to_udp(con, data, data_len); + } +} + +int get_local_num_ports (int addr_type) +{ + switch (addr_type) { + case BADDR_TYPE_IPV4: return options.local_udp_num_ports; + case BADDR_TYPE_IPV6: return options.local_udp_ip6_num_ports; + default: ASSERT(0); return 0; + } +} + +BAddr get_local_addr (int addr_type) +{ + ASSERT(get_local_num_ports(addr_type) >= 0) + + switch (addr_type) { + case BADDR_TYPE_IPV4: return local_udp_addr; + case BADDR_TYPE_IPV6: return local_udp_ip6_addr; + default: ASSERT(0); return BAddr_MakeNone(); + } +} + +uint8_t * build_port_usage_array_and_find_least_used_connection (BAddr remote_addr, struct connection **out_con) +{ + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(get_local_num_ports(remote_addr.type) >= 0) + + int local_num_ports = get_local_num_ports(remote_addr.type); + + // allocate port usage array + uint8_t *port_usage = (uint8_t *)BAllocSize(bsize_fromint(local_num_ports)); + if (!port_usage) { + return NULL; + } + + // zero array + memset(port_usage, 0, local_num_ports); + + struct connection *least_con = NULL; + + // flag inappropriate ports (those with the same remote address) + for (LinkedList1Node *ln = LinkedList1_GetFirst(&clients_list); ln; ln = LinkedList1Node_Next(ln)) { + struct client *client = UPPER_OBJECT(ln, struct client, clients_list_node); + + for (LinkedList1Node *ln2 = LinkedList1_GetFirst(&client->connections_list); ln2; ln2 = LinkedList1Node_Next(ln2)) { + struct connection *con = UPPER_OBJECT(ln2, struct connection, connections_list_node); + ASSERT(con->client == client) + ASSERT(!con->closing) + + if (con->addr.type != remote_addr.type || con->local_port_index < 0) { + continue; + } + ASSERT(con->local_port_index < local_num_ports) + + if (options.unique_local_ports) { + BIPAddr ip1; + BIPAddr ip2; + BAddr_GetIPAddr(&con->addr, &ip1); + BAddr_GetIPAddr(&remote_addr, &ip2); + if (!BIPAddr_Compare(&ip1, &ip2)) { + continue; + } + } else { + if (!BAddr_Compare(&con->addr, &remote_addr)) { + continue; + } + } + + port_usage[con->local_port_index] = 1; + + if (!PacketPassFairQueueFlow_IsBusy(&con->send_qflow)) { + if (!least_con || con->last_use_time < least_con->last_use_time) { + least_con = con; + } + } + } + } + + *out_con = least_con; + return port_usage; +} + +void connection_init (struct client *client, uint16_t conid, BAddr addr, BAddr orig_addr, const uint8_t *data, int data_len) +{ + ASSERT(client->num_connections < options.max_connections_for_client) + ASSERT(!find_connection(client, conid)) + BAddr_Assert(&addr); + ASSERT(addr.type == BADDR_TYPE_IPV4 || addr.type == BADDR_TYPE_IPV6) + ASSERT(orig_addr.type == BADDR_TYPE_IPV4 || orig_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + // allocate structure + struct connection *con = (struct connection *)malloc(sizeof(*con)); + if (!con) { + client_log(client, BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = client; + con->conid = conid; + con->addr = addr; + con->orig_addr = orig_addr; + con->first_data = data; + con->first_data_len = data_len; + + // set last use time + con->last_use_time = btime_gettime(); + + // set not closing + con->closing = 0; + + // init first job + BPending_Init(&con->first_job, BReactor_PendingGroup(&ss), (BPending_handler)connection_first_job_handler, con); + BPending_Set(&con->first_job); + + // init send queue flow + PacketPassFairQueueFlow_Init(&con->send_qflow, &client->send_queue); + + // init send PacketProtoFlow + if (!PacketProtoFlow_Init(&con->send_ppflow, udpgw_mtu, CONNECTION_CLIENT_BUFFER_SIZE, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); + + // init UDP dgram + if (!BDatagram_Init(&con->udp_dgram, addr.type, &ss, con, (BDatagram_handler)connection_dgram_handler_event)) { + client_log(client, BLOG_ERROR, "BDatagram_Init failed"); + goto fail2; + } + + con->local_port_index = -1; + + int local_num_ports = get_local_num_ports(addr.type); + + if (local_num_ports >= 0) { + // build port usage array, find least used connection + struct connection *least_con; + uint8_t *port_usage = build_port_usage_array_and_find_least_used_connection(addr, &least_con); + if (!port_usage) { + client_log(client, BLOG_ERROR, "build_port_usage_array failed"); + goto failed; + } + + // set SO_REUSEADDR + if (!BDatagram_SetReuseAddr(&con->udp_dgram, 1)) { + client_log(client, BLOG_ERROR, "set SO_REUSEADDR failed"); + goto failed; + } + + // get starting local address + BAddr local_addr = get_local_addr(addr.type); + + // try different ports + for (int i = 0; i < local_num_ports; i++) { + // skip inappropriate ports + if (port_usage[i]) { + continue; + } + + BAddr bind_addr = local_addr; + BAddr_SetPort(&bind_addr, hton16(ntoh16(BAddr_GetPort(&bind_addr)) + (uint16_t)i)); + if (BDatagram_Bind(&con->udp_dgram, bind_addr)) { + // remember which port we're using + con->local_port_index = i; + goto cont; + } + } + + // try closing an unused connection with the same remote addr + if (!least_con) { + goto failed; + } + + ASSERT(least_con->addr.type == addr.type) + ASSERT(least_con->local_port_index >= 0) + ASSERT(least_con->local_port_index < local_num_ports) + ASSERT(!PacketPassFairQueueFlow_IsBusy(&least_con->send_qflow)) + + int i = least_con->local_port_index; + + BLog(BLOG_INFO, "closing connection for its remote address"); + + // close the offending connection + connection_close(least_con); + + // try binding to its port + BAddr bind_addr = local_addr; + BAddr_SetPort(&bind_addr, hton16(ntoh16(BAddr_GetPort(&bind_addr)) + (uint16_t)i)); + if (BDatagram_Bind(&con->udp_dgram, bind_addr)) { + // remember which port we're using + con->local_port_index = i; + goto cont; + } + + failed: + client_log(client, BLOG_WARNING, "failed to bind to any local address; proceeding regardless"); + cont:; + BFree(port_usage); + } + + // set UDP dgram send address + BIPAddr ipaddr; + BIPAddr_InitInvalid(&ipaddr); + BDatagram_SetSendAddrs(&con->udp_dgram, addr, ipaddr); + + // init UDP dgram interfaces + BDatagram_SendAsync_Init(&con->udp_dgram, options.udp_mtu); + BDatagram_RecvAsync_Init(&con->udp_dgram, options.udp_mtu); + + // init UDP writer + BufferWriter_Init(&con->udp_send_writer, options.udp_mtu, BReactor_PendingGroup(&ss)); + + // init UDP buffer + if (!PacketBuffer_Init(&con->udp_send_buffer, BufferWriter_GetOutput(&con->udp_send_writer), BDatagram_SendAsync_GetIf(&con->udp_dgram), CONNECTION_UDP_BUFFER_SIZE, BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "PacketBuffer_Init failed"); + goto fail4; + } + + // init UDP recv interface + PacketPassInterface_Init(&con->udp_recv_if, options.udp_mtu, (PacketPassInterface_handler_send)connection_udp_recv_if_handler_send, con, BReactor_PendingGroup(&ss)); + + // init UDP recv buffer + if (!SinglePacketBuffer_Init(&con->udp_recv_buffer, BDatagram_RecvAsync_GetIf(&con->udp_dgram), &con->udp_recv_if, BReactor_PendingGroup(&ss))) { + client_log(client, BLOG_ERROR, "SinglePacketBuffer_Init failed"); + goto fail5; + } + + // insert to client's connections tree + ASSERT_EXECUTE(BAVL_Insert(&client->connections_tree, &con->connections_tree_node, NULL)) + + // insert to client's connections list + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // increment number of connections + client->num_connections++; + + connection_log(con, BLOG_DEBUG, "initialized"); + + return; + +fail5: + PacketPassInterface_Free(&con->udp_recv_if); + PacketBuffer_Free(&con->udp_send_buffer); +fail4: + BufferWriter_Free(&con->udp_send_writer); + BDatagram_RecvAsync_Free(&con->udp_dgram); + BDatagram_SendAsync_Free(&con->udp_dgram); + BDatagram_Free(&con->udp_dgram); +fail2: + PacketProtoFlow_Free(&con->send_ppflow); +fail1: + PacketPassFairQueueFlow_Free(&con->send_qflow); + BPending_Free(&con->first_job); + free(con); +fail0: + return; +} + +void connection_free (struct connection *con) +{ + struct client *client = con->client; + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + if (con->closing) { + // remove from client's closing connections list + LinkedList1_Remove(&client->closing_connections_list, &con->closing_connections_list_node); + } else { + // decrement number of connections + client->num_connections--; + + // remove from client's connections list + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + + // remove from client's connections tree + BAVL_Remove(&client->connections_tree, &con->connections_tree_node); + + // free UDP + connection_free_udp(con); + } + + // free send PacketProtoFlow + PacketProtoFlow_Free(&con->send_ppflow); + + // free send queue flow + PacketPassFairQueueFlow_Free(&con->send_qflow); + + // free first job + BPending_Free(&con->first_job); + + // free structure + free(con); +} + +void connection_logfunc (struct connection *con) +{ + client_logfunc(con->client); + + if (con->closing) { + BLog_Append("old connection %"PRIu16": ", con->conid); + } else { + BLog_Append("connection %"PRIu16": ", con->conid); + } +} + +void connection_log (struct connection *con, int level, const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + BLog_LogViaFuncVarArg((BLog_logfunc)connection_logfunc, con, BLOG_CURRENT_CHANNEL, level, fmt, vl); + va_end(vl); +} + +void connection_free_udp (struct connection *con) +{ + // free UDP receive buffer + SinglePacketBuffer_Free(&con->udp_recv_buffer); + + // free UDP receive interface + PacketPassInterface_Free(&con->udp_recv_if); + + // free UDP buffer + PacketBuffer_Free(&con->udp_send_buffer); + + // free UDP writer + BufferWriter_Free(&con->udp_send_writer); + + // free UDP dgram interfaces + BDatagram_RecvAsync_Free(&con->udp_dgram); + BDatagram_SendAsync_Free(&con->udp_dgram); + + // free UDP dgram + BDatagram_Free(&con->udp_dgram); +} + +void connection_first_job_handler (struct connection *con) +{ + ASSERT(!con->closing) + + connection_send_to_udp(con, con->first_data, con->first_data_len); +} + +void connection_send_to_client (struct connection *con, uint8_t flags, const uint8_t *data, int data_len) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + size_t addr_len = (con->orig_addr.type == BADDR_TYPE_IPV6) ? sizeof(struct udpgw_addr_ipv6) : + (con->orig_addr.type == BADDR_TYPE_IPV4) ? sizeof(struct udpgw_addr_ipv4) : 0; + if (data_len > udpgw_mtu - (int)(sizeof(struct udpgw_header) + addr_len)) { + connection_log(con, BLOG_WARNING, "packet is too large, cannot send to client"); + return; + } + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(con->send_if, &out)) { + connection_log(con, BLOG_ERROR, "out of client buffer"); + return; + } + int out_pos = 0; + + if (con->orig_addr.type == BADDR_TYPE_IPV6) { + flags |= UDPGW_CLIENT_FLAG_IPV6; + } + + // write header + struct udpgw_header header; + header.flags = htol8(flags); + header.conid = htol16(con->conid); + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (con->orig_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = con->orig_addr.ipv4.ip; + addr_ipv4.addr_port = con->orig_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, con->orig_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = con->orig_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write message + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit written message + ASSERT(out_pos <= udpgw_mtu) + BufferWriter_EndPacket(con->send_if, out_pos); +} + +int connection_send_to_udp (struct connection *con, const uint8_t *data, int data_len) +{ + struct client *client = con->client; + ASSERT(!con->closing) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + connection_log(con, BLOG_DEBUG, "from client %d bytes", data_len); + + // set last use time + con->last_use_time = btime_gettime(); + + // move connection to front + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(&con->udp_send_writer, &out)) { + connection_log(con, BLOG_ERROR, "out of UDP buffer"); + return 0; + } + + // write message + memcpy(out, data, data_len); + + // submit written message + BufferWriter_EndPacket(&con->udp_send_writer, data_len); + + return 1; +} + +void connection_close (struct connection *con) +{ + struct client *client = con->client; + ASSERT(!con->closing) + + // if possible, free connection immediately + if (!PacketPassFairQueueFlow_IsBusy(&con->send_qflow)) { + connection_free(con); + return; + } + + connection_log(con, BLOG_DEBUG, "closing later"); + + // decrement number of connections + client->num_connections--; + + // remove from client's connections list + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + + // remove from client's connections tree + BAVL_Remove(&client->connections_tree, &con->connections_tree_node); + + // free UDP + connection_free_udp(con); + + // insert to client's closing connections list + LinkedList1_Append(&client->closing_connections_list, &con->closing_connections_list_node); + + // set busy handler + PacketPassFairQueueFlow_SetBusyHandler(&con->send_qflow, (PacketPassFairQueue_handler_busy)connection_send_qflow_busy_handler, con); + + // unset first job + BPending_Unset(&con->first_job); + + // set closing + con->closing = 1; +} + +void connection_send_qflow_busy_handler (struct connection *con) +{ + ASSERT(con->closing) + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + connection_log(con, BLOG_DEBUG, "closing finally"); + + // free connection + connection_free(con); +} + +void connection_dgram_handler_event (struct connection *con, int event) +{ + ASSERT(!con->closing) + + connection_log(con, BLOG_INFO, "UDP error"); + + // close connection + connection_close(con); +} + +void connection_udp_recv_if_handler_send (struct connection *con, uint8_t *data, int data_len) +{ + struct client *client = con->client; + ASSERT(!con->closing) + ASSERT(data_len >= 0) + ASSERT(data_len <= options.udp_mtu) + + connection_log(con, BLOG_DEBUG, "from UDP %d bytes", data_len); + + // set last use time + con->last_use_time = btime_gettime(); + + // move connection to front + LinkedList1_Remove(&client->connections_list, &con->connections_list_node); + LinkedList1_Append(&client->connections_list, &con->connections_list_node); + + // accept packet + PacketPassInterface_Done(&con->udp_recv_if); + + // send packet to client + connection_send_to_client(con, 0, data, data_len); +} + +struct connection * find_connection (struct client *client, uint16_t conid) +{ + BAVLNode *tree_node = BAVL_LookupExact(&client->connections_tree, &conid); + if (!tree_node) { + return NULL; + } + struct connection *con = UPPER_OBJECT(tree_node, struct connection, connections_tree_node); + ASSERT(con->conid == conid) + ASSERT(!con->closing) + + return con; +} + +int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + return B_COMPARE(*v1, *v2); +} + +void maybe_update_dns (void) +{ +#ifndef BADVPN_USE_WINAPI + btime_t now = btime_gettime(); + if (now < btime_add(last_dns_update_time, DNS_UPDATE_TIME)) { + return; + } + last_dns_update_time = now; + BLog(BLOG_DEBUG, "update dns"); + + if (res_init() != 0) { + BLog(BLOG_ERROR, "res_init failed"); + goto fail; + } + + if (_res.nscount == 0) { + BLog(BLOG_ERROR, "no name servers available"); + goto fail; + } + + BAddr addr; + BAddr_InitIPv4(&addr, _res.nsaddr_list[0].sin_addr.s_addr, hton16(53)); + + if (!BAddr_Compare(&addr, &dns_addr)) { + char str[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&addr, str); + BLog(BLOG_INFO, "using DNS server %s", str); + } + + dns_addr = addr; + return; + +fail: + BAddr_InitNone(&dns_addr); +#endif +} diff --git a/external/badvpn_dns/udpgw/udpgw.h b/external/badvpn_dns/udpgw/udpgw.h new file mode 100644 index 00000000..f63f857c --- /dev/null +++ b/external/badvpn_dns/udpgw/udpgw.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) Ambroz Bizjak + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// name of the program +#define PROGRAM_NAME "udpgw" + +// maxiumum listen addresses +#define MAX_LISTEN_ADDRS 16 + +// maximum datagram size +#define DEFAULT_UDP_MTU 65520 + +// connection buffer size for sending to client, in packets +#define CONNECTION_CLIENT_BUFFER_SIZE 1 + +// connection buffer size for sending to UDP, in packets +#define CONNECTION_UDP_BUFFER_SIZE 1 + +// maximum number of clients +#define DEFAULT_MAX_CLIENTS 3 + +// maximum connections for client +#define DEFAULT_MAX_CONNECTIONS_FOR_CLIENT 256 + +// how long after nothing has been received to disconnect a client +#define CLIENT_DISCONNECT_TIMEOUT 20000 + +// SO_SNDBFUF socket option for clients, 0 to not set +#define CLIENT_DEFAULT_SOCKET_SEND_BUFFER 1048576 diff --git a/external/badvpn_dns/udpgw_client/CMakeLists.txt b/external/badvpn_dns/udpgw_client/CMakeLists.txt new file mode 100644 index 00000000..59ac6360 --- /dev/null +++ b/external/badvpn_dns/udpgw_client/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(udpgw_client "system;flow;flowextra" "" UdpGwClient.c) diff --git a/external/badvpn_dns/udpgw_client/UdpGwClient.c b/external/badvpn_dns/udpgw_client/UdpGwClient.c new file mode 100644 index 00000000..85d069ad --- /dev/null +++ b/external/badvpn_dns/udpgw_client/UdpGwClient.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2); +static void free_server (UdpGwClient *o); +static void decoder_handler_error (UdpGwClient *o); +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len); +static void send_monitor_handler (UdpGwClient *o); +static void keepalive_if_handler_done (UdpGwClient *o); +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid); +static uint16_t find_unused_conid (UdpGwClient *o); +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len); +static void connection_free (struct UdpGwClient_connection *con); +static void connection_first_job_handler (struct UdpGwClient_connection *con); +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len); +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); + +static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) +{ + return B_COMPARE(*v1, *v2); +} + +static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2) +{ + int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); + if (r) { + return r; + } + return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); +} + +static void free_server (UdpGwClient *o) +{ + // disconnect send connector + PacketPassConnector_DisconnectOutput(&o->send_connector); + + // free send sender + PacketStreamSender_Free(&o->send_sender); + + // free receive decoder + PacketProtoDecoder_Free(&o->recv_decoder); + + // free receive interface + PacketPassInterface_Free(&o->recv_if); +} + +static void decoder_handler_error (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + BLog(BLOG_ERROR, "decoder error"); + + // report error + o->handler_servererror(o->user); + return; +} + +static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udpgw_mtu) + + // accept packet + PacketPassInterface_Done(&o->recv_if); + + // check header + if (data_len < sizeof(struct udpgw_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct udpgw_header header; + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + uint8_t flags = ltoh8(header.flags); + uint16_t conid = ltoh16(header.conid); + + // parse address + BAddr remote_addr; + if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { + if (data_len < sizeof(struct udpgw_addr_ipv6)) { + BLog(BLOG_ERROR, "missing ipv6 address"); + return; + } + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); + BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); + } else { + if (data_len < sizeof(struct udpgw_addr_ipv4)) { + BLog(BLOG_ERROR, "missing ipv4 address"); + return; + } + struct udpgw_addr_ipv4 addr_ipv4; + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); + BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); + } + + // check remaining data + if (data_len > o->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // find connection + struct UdpGwClient_connection *con = find_connection_by_conid(o, conid); + if (!con) { + BLog(BLOG_ERROR, "unknown conid"); + return; + } + + // check remote address + if (BAddr_CompareOrder(&con->conaddr.remote_addr, &remote_addr) != 0) { + BLog(BLOG_ERROR, "wrong remote address"); + return; + } + + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // pass packet to user + o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len); + return; +} + +static void send_monitor_handler (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + + if (o->keepalive_sending) { + return; + } + + BLog(BLOG_INFO, "keepalive"); + + // send keepalive + PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet)); + + // set sending keep-alive + o->keepalive_sending = 1; +} + +static void keepalive_if_handler_done (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->keepalive_sending) + + // set not sending keepalive + o->keepalive_sending = 0; +} + +static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node); +} + +static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node); +} + +static uint16_t find_unused_conid (UdpGwClient *o) +{ + ASSERT(o->num_connections < o->max_connections) + + while (1) { + if (!find_connection_by_conid(o, o->next_conid)) { + return o->next_conid; + } + + if (o->next_conid == o->max_connections - 1) { + o->next_conid = 0; + } else { + o->next_conid++; + } + } +} + +static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len) +{ + ASSERT(o->num_connections < o->max_connections) + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // allocate structure + struct UdpGwClient_connection *con = (struct UdpGwClient_connection *)malloc(sizeof(*con)); + if (!con) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = o; + con->conaddr = conaddr; + con->first_flags = flags; + con->first_data = data; + con->first_data_len = data_len; + + // allocate conid + con->conid = find_unused_conid(o); + + // init first job + BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con); + BPending_Set(&con->first_job); + + // init queue flow + PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue); + + // init PacketProtoFlow + if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) { + BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); + goto fail1; + } + con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + // insert to connections tree by conid + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL)) + + // insert to connections list + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // increment number of connections + o->num_connections++; + + return; + +fail1: + PacketPassFairQueueFlow_Free(&con->send_qflow); + BPending_Free(&con->first_job); + free(con); +fail0: + return; +} + +static void connection_free (struct UdpGwClient_connection *con) +{ + UdpGwClient *o = con->client; + PacketPassFairQueueFlow_AssertFree(&con->send_qflow); + + // decrement number of connections + o->num_connections--; + + // remove from connections list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + + // remove from connections tree by conid + BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // free PacketProtoFlow + PacketProtoFlow_Free(&con->send_ppflow); + + // free queue flow + PacketPassFairQueueFlow_Free(&con->send_qflow); + + // free first job + BPending_Free(&con->first_job); + + // free structure + free(con); +} + +static void connection_first_job_handler (struct UdpGwClient_connection *con) +{ + connection_send(con, UDPGW_CLIENT_FLAG_REBIND|con->first_flags, con->first_data, con->first_data_len); +} + +static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len) +{ + UdpGwClient *o = con->client; + B_USE(o) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // get buffer location + uint8_t *out; + if (!BufferWriter_StartPacket(con->send_if, &out)) { + BLog(BLOG_ERROR, "out of buffer"); + return; + } + int out_pos = 0; + + if (con->conaddr.remote_addr.type == BADDR_TYPE_IPV6) { + flags |= UDPGW_CLIENT_FLAG_IPV6; + } + + // write header + struct udpgw_header header; + header.flags = ltoh8(flags); + header.conid = ltoh16(con->conid); + memcpy(out + out_pos, &header, sizeof(header)); + out_pos += sizeof(header); + + // write address + switch (con->conaddr.remote_addr.type) { + case BADDR_TYPE_IPV4: { + struct udpgw_addr_ipv4 addr_ipv4; + addr_ipv4.addr_ip = con->conaddr.remote_addr.ipv4.ip; + addr_ipv4.addr_port = con->conaddr.remote_addr.ipv4.port; + memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); + out_pos += sizeof(addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + struct udpgw_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr_ip, con->conaddr.remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); + addr_ipv6.addr_port = con->conaddr.remote_addr.ipv6.port; + memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); + out_pos += sizeof(addr_ipv6); + } break; + } + + // write packet to buffer + memcpy(out + out_pos, data, data_len); + out_pos += data_len; + + // submit packet to buffer + BufferWriter_EndPacket(con->send_if, out_pos); +} + +static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) +{ + ASSERT(!find_connection_by_conaddr(o, conaddr)) + ASSERT(o->num_connections > 0) + + // get least recently used connection + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + + // remove from connections tree by conaddr + BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); + + // set new conaddr + con->conaddr = conaddr; + + // insert to connections tree by conaddr + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) + + return con; +} + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) +{ + ASSERT(udp_mtu >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) >= 0) + ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD) + ASSERT(max_connections > 0) + ASSERT(send_buffer_size > 0) + + // init arguments + o->udp_mtu = udp_mtu; + o->max_connections = max_connections; + o->send_buffer_size = send_buffer_size; + o->keepalive_time = keepalive_time; + o->reactor = reactor; + o->user = user; + o->handler_servererror = handler_servererror; + o->handler_received = handler_received; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // compute MTUs + o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); + o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header); + + // init connections tree by conaddr + BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL); + + // init connections tree by conid + BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL); + + // init connections list + LinkedList1_Init(&o->connections_list); + + // set zero connections + o->num_connections = 0; + + // set next conid + o->next_conid = 0; + + // init send connector + PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // init send monitor + PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o); + + // init send queue + if (!PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1)) { + goto fail0; + } + + // construct keepalive packet + o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw); + memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw)); + o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE; + + // init keepalive queue flow + PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue); + o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow); + + // init keepalive output + PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o); + + // set not sending keepalive + o->keepalive_sending = 0; + + // set have no server + o->have_server = 0; + + DebugObject_Init(&o->d_obj); + return 1; + +fail0: + PacketPassInactivityMonitor_Free(&o->send_monitor); + PacketPassConnector_Free(&o->send_connector); + return 0; +} + +void UdpGwClient_Free (UdpGwClient *o) +{ + DebugObject_Free(&o->d_obj); + + // allow freeing send queue flows + PacketPassFairQueue_PrepareFree(&o->send_queue); + + // free connections + while (!LinkedList1_IsEmpty(&o->connections_list)) { + struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); + connection_free(con); + } + + // free server + if (o->have_server) { + free_server(o); + } + + // free keepalive queue flow + PacketPassFairQueueFlow_Free(&o->keepalive_qflow); + + // free send queue + PacketPassFairQueue_Free(&o->send_queue); + + // free send + PacketPassInactivityMonitor_Free(&o->send_monitor); + + // free send connector + PacketPassConnector_Free(&o->send_connector); +} + +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + // build conaddr + struct UdpGwClient_conaddr conaddr; + conaddr.local_addr = local_addr; + conaddr.remote_addr = remote_addr; + + // lookup connection + struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr); + + uint8_t flags = 0; + + if (is_dns) { + // route to remote DNS server instead of provided address + flags |= UDPGW_CLIENT_FLAG_DNS; + } + + // if no connection and can't create a new one, reuse the least recently used une + if (!con && o->num_connections == o->max_connections) { + con = reuse_connection(o, conaddr); + flags |= UDPGW_CLIENT_FLAG_REBIND; + } + + if (!con) { + // create new connection + connection_init(o, conaddr, flags, data, data_len); + } else { + // move connection to front of the list + LinkedList1_Remove(&o->connections_list, &con->connections_list_node); + LinkedList1_Append(&o->connections_list, &con->connections_list_node); + + // send packet to existing connection + connection_send(con, flags, data, data_len); + } +} + +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) +{ + DebugObject_Access(&o->d_obj); + ASSERT(!o->have_server) + + // init receive interface + PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor)); + + // init receive decoder + if (!PacketProtoDecoder_Init(&o->recv_decoder, recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { + BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); + goto fail1; + } + + // init send sender + PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor)); + + // connect send connector + PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender)); + + // set have server + o->have_server = 1; + + return 1; + +fail1: + PacketPassInterface_Free(&o->recv_if); + return 0; +} + +void UdpGwClient_DisconnectServer (UdpGwClient *o) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->have_server) + + // free server + free_server(o); + + // set have no server + o->have_server = 0; +} diff --git a/external/badvpn_dns/udpgw_client/UdpGwClient.h b/external/badvpn_dns/udpgw_client/UdpGwClient.h new file mode 100644 index 00000000..0a1086bf --- /dev/null +++ b/external/badvpn_dns/udpgw_client/UdpGwClient.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Ambroz Bizjak + * Contributions: + * Transparent DNS: Copyright (C) Kerem Hadimli + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H +#define BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*UdpGwClient_handler_servererror) (void *user); +typedef void (*UdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +B_START_PACKED +struct UdpGwClient__keepalive_packet { + struct packetproto_header pp; + struct udpgw_header udpgw; +} B_PACKED; +B_END_PACKED + +typedef struct { + int udp_mtu; + int max_connections; + int send_buffer_size; + btime_t keepalive_time; + BReactor *reactor; + void *user; + UdpGwClient_handler_servererror handler_servererror; + UdpGwClient_handler_received handler_received; + int udpgw_mtu; + int pp_mtu; + BAVL connections_tree_by_conaddr; + BAVL connections_tree_by_conid; + LinkedList1 connections_list; + int num_connections; + int next_conid; + PacketPassFairQueue send_queue; + PacketPassInactivityMonitor send_monitor; + PacketPassConnector send_connector; + struct UdpGwClient__keepalive_packet keepalive_packet; + PacketPassInterface *keepalive_if; + PacketPassFairQueueFlow keepalive_qflow; + int keepalive_sending; + int have_server; + PacketStreamSender send_sender; + PacketProtoDecoder recv_decoder; + PacketPassInterface recv_if; + DebugObject d_obj; +} UdpGwClient; + +struct UdpGwClient_conaddr { + BAddr local_addr; + BAddr remote_addr; +}; + +struct UdpGwClient_connection { + UdpGwClient *client; + struct UdpGwClient_conaddr conaddr; + uint8_t first_flags; + const uint8_t *first_data; + int first_data_len; + uint16_t conid; + BPending first_job; + BufferWriter *send_if; + PacketProtoFlow send_ppflow; + PacketPassFairQueueFlow send_qflow; + BAVLNode connections_tree_by_conaddr_node; + BAVLNode connections_tree_by_conid_node; + LinkedList1Node connections_list_node; +}; + +int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, + UdpGwClient_handler_servererror handler_servererror, + UdpGwClient_handler_received handler_received) WARN_UNUSED; +void UdpGwClient_Free (UdpGwClient *o); +void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len); +int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) WARN_UNUSED; +void UdpGwClient_DisconnectServer (UdpGwClient *o); + +#endif diff --git a/jni/Android.mk b/jni/Android.mk index 178e692c..f8ca80fc 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -1,3 +1,3 @@ ##include ../OriginalDest/Android.mk -include ./external/badvpn_dnsfix/Android.mk +include ./external/badvpn_dns/Android.mk ##include ../kalium-jni/jni/Android.mk