Home

Previous 20

Jul. 29th, 2007

svchost.exe WORM_BACTERA.I

http://www.trendmicro.com/vinfo/virusencyclo/default5.asp?VName=WORM_BACTERA.I

Jul. 23rd, 2007

advice from xupeng

用COOIKE试一下,就是在一个单点登陆的地方下一个COOIKE,然后另外两个系统检测这个COOIKE. 注意把COOIKE加密

必须让用户都读写一个表

就是说,所以有数据库的用户信息,都要放到一个数据库的表里

Jun. 15th, 2007

Linux Distribution Chooser

http://www.zegeniestudios.net/ldc/index.php?lang=en

Jun. 14th, 2007

Creating a Single Sign-on for ASP.NET Application and Legacy ASP Application (Part II)

From http://www.wwwcoder.com/Directory/tabid/68/type/art/site/4922/parentid/258/Default.aspx

A hack into the .NET authentication classes to extract a component that can be called by legacy asp applications to generate the same authentication token used in asp.net applications.

Ting Huang

A hack into the .NET authentication classes to extract a component that can be called by legacy asp applications to generate the same authentication token used in asp.net applications.

(Link to part I: http://www.wwwcoder.com/main/parentid/258/site/2598/68/default.aspx)

Introduction

In part I of the article, I introduced a way to create a single sign-on between asp and asp.net applications. Although the solution is sound, it is not without shortcomings:

1. While the authentication token generated is different each time, it is not time-stamped. Hence, there is no expiration by itself. This could lead to a high risk of being maliciously reused when the token is hijacked.

2. It is not a time-saving or effort-saving solution, because additional development is required on both sides of asp and asp.net applications to manage the authentication token.

It would be ideal if the forms authentication mechanism from ASP.NET can be utilized by ASP application to generate or verify authentication token efficiently. Additionally, the token generated should be automatically recognized as a valid token by ASP.NET built-in forms authentication mechanism as well.

Inspired by a Microsoft knowledge-base article at http://support.microsoft.com/default.aspx?scid=kb;EN-US;891028 which makes a web service to provide authentication methods for ASP application, I got the idea of using the asp.net FormsAuthentication utilities directly in classic asp. To circumvent the initialization problem, I extracted the core functionalities apart from the FormsAuthentication utilities and removed the requirements for HttpContext and other related classes that are only available in asp.net. The asp scripts will be able to use simple COM interop to call into this authentication component to retrieve as well as verify tokens.

How Forms Authentication Works in ASP.NET

What happens behind the scene of the form authentication mechanism in ASP.NET is that when a client request goes through the http pipeline, the FormsAuthenticationModule (a HttpModule) picks up the incoming request and tries to extract authentication ticket from cookie. The process requires decryption and reading configurations via HttpContext.

Solution

The solution is to extract the core functionalities apart from the FormsAuthentication utilities in ASP.NET and remove the requirements for HttpContext and other related classes and make them into a authentication component that can be called by ASP application.

Here is how the solution looks like:

The following steps show how to get the solution working.

1. Synchronize the encryption and decryption key

Forms authentication in asp.net uses the <machineKey> element in machine.config or web.config for encryption and decryption authentication token:

<machineKey validationKey="AutoGenerate|value[,IsolateApps]" decryptionKey="AutoGenerate|value[,IsolateApps] validation="SHA1|MD5|3DES"/>

If you have run ASP.NET applications in a server farm, you probably know that the machine key has to be set with the same value on all severs in the farm. This ensures that tokens generated on one of the servers can be validated on another. For the same reason, it is necessary to synchronize the encryption and decryption key in both ASP.NET forms authentication and the custom authentication component.

The following link can be used to generate a machine key setting:

http://www.eggheadcafe.com/articles/GenerateMachineKey/GenerateMachineKey.aspx

The following is an example of such setting:

<machineKey validationKey='6C0D4C65DBD3CF60C037E0BE43D2958954DA9AAA830D902B8481DDDF962A
4E392056DE5EEEEC0061673BB413DB533D6F8C61C70AD36C9F89F99DC0B3AD292B53'  
decryptionKey='7EBC8C30854618249344BAF797E1B9DD119F249D459C006D'   validation='SHA1'/>

This is the only change has to be made in ASP.NET for the single sign-on to work.

2. Create a COM wrapper for authentication

This is the ‘bare bone’ class for the authentication API.

public class AuthAPI
{
public AuthAPI()
      {} 

      public void Initialize()
      {
            FormsAuthentication.Initialize();
      }
       public void Initialize1(
            string cookieName,
            string cookieDomain,
            string cookiePath,
            bool slidingExpiration,
            int timeout,
            bool requireSSL
            )
      {
            FormsAuthentication.Initialize(
                  cookieName,
                  cookieDomain,
                  cookiePath,
                  slidingExpiration,
                  timeout,
                  requireSSL
                  );
      } 

      public void Initialize2(
            string cookieName,
            string cookieDomain,
            string cookiePath,
            bool slidingExpiration,
            string protection,
            int timeout,
            bool requireSSL,
            string validationKey,
            string decryptionKey,
            string validationMode
            )
      {
            FormsAuthentication.Initialize(
                  cookieName,
                  cookieDomain,
                  cookiePath,
                  slidingExpiration,
                  (FormsProtectionEnum)Enum.Parse(typeof(FormsProtectionEnum), protection, true),
                  timeout,
                  requireSSL,
                  validationKey,
                  decryptionKey,
                  (FormsCryptoValidationMode)Enum.Parse(typeof(FormsCryptoValidationMode), validationMode, true)
                  );
      }

      public string GetAuthCookieValue(
            string
userName,
            bool
createPersistentCookie)
      {
            HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
            return cookie.Value;
      }

       public bool IsAuthenticated(
            string
cookieValue,
            out
string newCookieValue,
            out
DateTime expiration)
      {
            return FormsAuthentication.IsAuthenticated(cookieValue, out newCookieValue, out expiration);
      }
}

Initialize2 method requires validationKey, decryptionKey and validationMode as well as other parameters being passed in order for the custom FormsAuthentication to be initialized. The two other initialization methods will use the default keys in the class. To make the COM call easier, don’t use overloads in C# for the initialization methods.

GetAuthCookieValue method works almost the same as GetAuthCookie in ASP.NET FormsAuthentication class except that it doesn’t return a HttpCookie object but rather a string value of the cookie to be written directly to client’s browser.

IsAuthenticated method validates the incoming cookie if it is a valid authentication cookie. If a renew action is required for the cookie on the caller, it will return value in the newCookieValue parameter, otherwise, it will be empty. The expiration DateTime value gives convenience to the caller when it performs the renew action.

3. Making a new asp authentication script

To retrieve the authentication cookie:

<%          cookieName               =          ".ASPXAUTH"
            cookieDomain             =          "yourdomain"
            cookiePath               =          "/"

            Set ulogin = Server.CreateObject("SingleSignon.AuthAPI")
            uLogin.Initialize1 cookieName, cookieDomain, cookiePath, 1, 20, 0
            s = uLogin.GetAuthCookieValue(cookieName, false)
            Call AddCookie ( cookieName, s )
            set ulogin = nothing
%>

<%      Set ulogin = Server.CreateObject("SingleSignon.AuthAPI")
            isLogin =  uLogin.IsAuthenticated(GetCookie(uLogin.FormsCookieName, ""), newCookieValue, expiration)
            If not isLogin then
                        ‘ user is not logged in, redirect to login page
            Else
                        ‘ user is logged in
                        If not isempty(newCookieValue) then
                        'renew cookie
                                Call AddCookie ( cookieName, newCookieValue, expiration )
                        End If
            End If
            Set ulogin = nothing
%>

Conclusion

In this article, a custom authentication component is extracted from the ASP.NET forms authentication utilities to remove any HttpContext initialization so that it can be called directly in classic ASP scripts through COM interface. The authentication token obtained via this API component works between ASP.NET and ASP application, with minimum setting in ASP.NET’s forms authentication. It also works efficiently without a web service workaround.

There are more that can be done based on this solution. These include:

  1. Update the Authentication API to read configurations from ASP.NET application. This can either be done via web service or direct access to the config file in ASP.NET. The advantage is to avoid explicitly passing encryption keys;
  2. Use Session or any other available solutions to maintain the state, so that calling the Authentication API for every authentication request in ASP can be avoided.

References

For more information, please see the following resources:
ASP.NET security overview (http://support.microsoft.com/default.aspx?scid=kb;EN-US;891028 )

Download the code!

Tags: ,

Creating a Single Sign-on across ASP.NET Application and Legacy ASP Application

From http://www.wwwcoder.com/main/parentid/258/site/2598/68/default.aspx

Encryption and hashing are used in this project to solve a common problem faced by many programmers, which is to create a single sign-on across multiple applications, which might be a mix of legacy ASP applications and ASP.NET applications.

The Scenario

For many membership websites, it is a very common scenario to limit access of some services to members only and require users to login. But oftentimes these different services are served under different applications or even under different platforms. This time we want to solve the problem of making a unified login that can be shared among legacy ASP application and ASP.NET application.


The First Thought

The first thought is always to try to dig out anything that can be utilized in the .NET framework. The build-in FormsAuthentication class under System.Web.Security namespace is the best candidate. By calling the SetAuthCookie(string, bool) method in FormsAuthentication, an authentication ticket for the given user identity can be created and attached to the cookie's collection of the outgoing response. Later by checking the Request.IsAuthenticated value, the user's login status can be verified. This works very well among pure ASP.NET applications. But when it comes to ASP applications, these methods or objects are obviously not available, or I should say, not easy to get implemented in the same way that ASP.NET does. Since we really don¡¯t know the detail inside FormsAuthentication or related classes, there is no easy way we can modify or mimic these classes to make them available to classic ASP application.

How about sharing these classes in GAC that can be called by ASP script? The FormsAuthentication class fails when called from ASP script because it simply needs a FORM to be initialized. If the FormsAuthentication and related classes can be modified, we can probably get rid of some initialization requirements that are not needed.



The Solution

We need to make our own authentication class.

First we create the EncryptString class to serve the Obfuscation of the user's identity as well as to verify the Obfuscated string. A triple DES encryption and a couple of MD5 hashes are involved.</font>

using System;using System.Security.Cryptography;using System.Text;using System.Web;using System.Globalization;namespace SingleLogin{      /// <summary>   /// Provides a way to obfuscate the string.   /// </summary>   public class EncryptString   {      public EncryptString(){}         public bool Verify(string pEncryptedString)          {            return _Verify(pEncryptedString);         }         public string EncryptedString          {            get             {               return _Encrypt();            }         }         public string UserName          {            get             {               return _UserName;            }            set            {               _UserName = value;            }        }        public string Key         {            get             {               return _Key;            }            set            {               _Key = value;            }        }        private string _Encrypt()         {           MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();           byte[] bKey = MD5.ComputeHash(Encoding.ASCII.GetBytes(_Key));           byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName));           byte[] eUserName = MD5.ComputeHash(_Mix(bUserName, bKey));           TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();           byte[] eKey = des.CreateEncryptor(_Key192, _IV192).TransformFinalBlock(bKey, 0, bKey.Length);           return _ByteToHexString(eUserName) + _ByteToHexString(eKey);        }        private bool _Verify(string pEncryptedString)         {            try             {               byte[] eKey = _HexToByteArray(pEncryptedString.Substring(32));               byte[] eUserName = _HexToByteArray(pEncryptedString.Substring(0, 32));               MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();               TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();               byte[] bKey = des.CreateDecryptor(_Key192, _IV192).TransformFinalBlock(eKey, 0, eKey.Length);               byte[] bUserName = MD5.ComputeHash(Encoding.ASCII.GetBytes(_UserName));               byte[] eUserName1 = MD5.ComputeHash(_Mix(bUserName, bKey));               return (_ByteToHexString(eUserName) == _ByteToHexString(eUserName1));            }            catch (CryptographicException)             {            }            catch (FormatException)             {            }            return false;        }        private byte[] _Mix(byte[] pUserName, byte[] pKey)        {           byte[] retByteArray = new byte[pUserName.Length * 2];           int i = 0, j;           foreach(byte b in pUserName)           {              j = i * 2;              retByteArray[j] = pUserName[i];              retByteArray[j+1] = pKey[i];              i++;           }           return retByteArray;        }        private string _ByteToHexString(byte[] pByteArray)        {           string retHexString = "";           foreach(byte b in pByteArray)           {              retHexString += b.ToString("x2");           }           return retHexString;        }        private static byte[] _HexToByteArray(string pHexString)        {           int length = pHexString.Length/2;           byte[] retByteArray = new byte[length];           for(int i=0; i< length; i++)           {              retByteArray[i] = Byte.Parse(pHexString.Substring(i*2, 2), NumberStyles.HexNumber);           }           return retByteArray;        }		        // The key used for the triple DES encryption        private readonly byte[] _Key192 = new byte[24] {           142, 216, 90, 16, 178, 40, 8, 32,           35, 167, 34, 80, 226, 200, 125, 192,           2, 94, 51, 204, 139, 35, 14, 19};		        // The Initialization Vector for the triple DES encryption        private readonly byte[] _IV192 = new byte[24] {           54, 173, 246, 179, 36, 99, 197, 3,           42, 65, 62, 38, 134, 7, 29, 123, 145,           23, 200, 58, 193, 10, 111, 232};        private string _UserName, _Key;   }}

The process looks like the following:

The Authentication class then can be created:

using System;using System.Web;using System.Text;namespace SingleLogin{	    /// <summary>    /// Summary description for Authenticate.    /// </summary>    public class Authentication    {       public Authentication()       {       }       public bool IsAuthenticated(string pEncString, string pUserName)       {          EncryptString sqs = new EncryptString();          sqs.UserName = pUserName;          return sqs.Verify(pEncString);       }       public string GetAuthenticateCode(string pUserName)       {          EncryptString sqs = new EncryptString();          sqs.UserName = pUserName;          sqs.Key = _GenerateRandomKey(8);          return sqs.EncryptedString;       }       private string _GenerateRandomKey(int pKeyLength)       {          Random random = new Random((int)DateTime.Now.Ticks);          int charLength = chars.Length;          StringBuilder sb = new StringBuilder();          for (int i=0; i< pKeyLength; i++)           {             int idx = random.Next(0, charLength);             sb.Append(chars.Substring(idx, 1));          }          return sb.ToString();       }       private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";   }}

To test our solution in ASP application, we first register our assembly with GAC. a reference can be found at  http://msdn.microsoft.com/msdnmag/issues/01/08/interop/default.aspx with the part Using .NET Objects from COM.

The shortcut to register the assembly in GAC is to locate the SingleLogin.dll that can be built in our solution (see the attached solution package) and run the following two .NET commands:
regasm SingleLogin.dll
gacutil /i SingleLogin.dll

After that, you can run the following ASP script on the web server to test if the correct authenticated string can be generated based on the user's identity.

<%   Set test = Server.CreateObject("SingleLogin.Authentication")   ' get the authenticate string   cookie = test.GetAuthenticateCode("demo")   response.write cookie   ' test the authenticate string with the original username   name = test.IsAuthenticated(cookie, "demo")   response.write "<br>" & name   ' test the authenticate string with a wrong username   name = test.IsAuthenticated(cookie, "ting")   response.write "<br>" & name   set test = nothing%>

A similar ASP.NET web sample is also included in the attached solution package.

When the applications share the same authentication methods, a single sign-on can then be created among them.

Some Final Thoughts

There are some risks to authenticate users in the way we do. For example, the authentication cookie can be hijacked and the hijacker can reuse the cookie to authenticate himself. But this happens anyway even if you are authenticating users with the build-in FormsAuthentication class. Another vulnerability is the computed key can be compromised and used to generate the authentication ticket. In this case, the attacker has to compromise the triple DES encryption.

Still, there is plenty of space to think of for restructuring or customizing the process of encryption. Any comments are welcome here.

References

.NET Interop: Get Ready for Microsoft .NET by Using Wrappers to Interact with COM-based Applications
http://msdn.microsoft.com/msdnmag/issues/01/08/interop/default.aspx

Secure Query Strings: Preventing the Tampering of Data Passed between Applications
http://www.dotnetjunkies.com/how%20to/99201486-ACFD-4607-A0CC-99E75836DC72.dcik

Download the code!


About the Author:
Ting Huang, a senior programmer for a fast growing Minnesota based online marketing company and spends most of his time deep in system architect, ASP.NET and SQL Server.
Tags: ,

ASP.NET 2.0: Implementing Single Sign On (SSO) with Membership API

From http://blah.winsmarts.com/2006/05/19/aspnet-20-implementing-single-sign-on-sso-with-membership-api.aspx

The membership API is awesome. No doubt about that. But I wish it had a more obvious in-built support for SSO. The only authenticate method takes in a username and password, there is no support for a token based system. Also, if you did add another method to verify against a ticketing authority - the membership API simply ignores it.

So the question is, How to do SSO using the Membership API - custom provider or otherwise.

Now ASP.NET has 3 kinds of authentication -

Passport - nobody uses it anymore, and that is SSO by definition anyway.
Windows - SSO is like a moot point here.
Forms - This is where the problem begins. So thats all I'm gonna discuss here.

Well, single sign on is almost always implemented using a central ticketing authority. The idea being similar to the concept of visiting a bar. You get a "token" stamped on the back of your hand as you walk into the bar, and then everywhere in the bar, you are recognized as "over 21".

Similarly, in a ticketing based SSO implementation, a central ticketing authority issues you a "ticket" at logon. Then you can integrate with any other system by passing the "ticket", rather than your authentication credentials. Then as long as the protected system can verify the "ticket" as being valid, you're in. Else, you're out.

Anyway, so the first thing you need to do is setup a ticket verification webservice. The idea being, anytime you successfully authenticate, you will be issued a ticket, which will ideally be a GUID, stored as cookie that expires when the user hits the CROSS button on his browser. Then you can circumvent the membership API's "Authenticate" method, by instead verifying the ticket - IF one is present.

So lets assume that the ticket is being passed as a querystring called "ssoToken". Obviously, if someone else sniffed your ticketID, then he could masquerade as you - so this approach requires some kind of encryption.

So in the Page_Load for your login.aspx page, write the following code -

protected void Page_Load(object sender, EventArgs e)

{

    string unescapedtokenID = Uri.UnescapeDataString(Request.QueryString["ReturnURL"]);

    string tokenID = ParseURL(unescapedtokenID);

 

 

    if (IsTokenValid(tokenID))

    {

        FormsAuthentication.SetAuthCookie(GetUserName(tokenID), false);

        Response.Redirect(unescapedtokenID);

    }

}

 

Basically "tokenID" is the token that was passed in as QueryString (or any other means), and IsTokenValid queries the ticketing web service to check the validity of the ticket. The ParseURL method is simply some magic to seperate out querystring peices out of the URL contained in the "ReturnURL" query string. If you're interested, the code for that looks like as below -

    private string ParseURL(string unescapedTokenID)

    {

        UriBuilder bldr = new UriBuilder("http://dummyurl" + unescapedTokenID);

        QueryStringParser coll = new QueryStringParser(bldr.Query);

        return coll["ssoToken"];

    }

 

The QueryStringParser class looks like this -

internal class QueryStringParser : System.Collections.Specialized.NameValueCollection

{

    internal QueryStringParser(string s)

    {

        if (s.Length != 0) s = s.Substring(1);

        int num1 = (s != null) ? s.Length : 0;

        for (int num2 = 0; num2 < num1; num2++)

        {

            int num3 = num2;

            int num4 = -1;

            while (num2 < num1)

            {

                char ch1 = s[num2];

                if (ch1 == '=')

                {

                    if (num4 < 0)

                    {

                        num4 = num2;

                    }

                }

                else if (ch1 == '&')

                {

                    break;

                }

                num2++;

            }

            string text1 = null;

            string text2 = null;

            if (num4 >= 0)

            {

                text1 = s.Substring(num3, num4 - num3);

                text2 = s.Substring(num4 + 1, (num2 - num4) - 1);

            }

            else

            {

                text2 = s.Substring(num3, num2 - num3);

            }

           

            base.Add(HttpUtility.UrlDecode(text1, Encoding.ASCII), HttpUtility.UrlDecode(text2, Encoding.ASCII));

 

            if ((num2 == (num1 - 1)) && (s[num2] == '&'))

            {

                base.Add(null, string.Empty);

            }

        }

    }

}

Thats it !! Put all these together, and you have at your hands SSO using Membership API.

Tags: ,

Single Sign-On for everyone

From http://bbs.hidotnet.com/22656/ShowPost.aspx

Single Sign-On (SSO) is a hot topic these days. Most clients I worked with have more than one web application running under different versions of .NET framework in different subdomains, or even in different domains and they want to let the user login once and stay logged in when switching to a different web site. Today we will implement SSO and see if we can make it work in different scenarios. We will start with a simple case and gradually build upon it:

  1. SSO for parent and child application in the virtual sub-directory
  2. SSO using different authorization credentials (username mapping)
  3. SSO for two applications in two sub-domains of the same domain
  4. SSO when applications run under different versions of .NET
  5. SSO for two applications in different domains.
  6. SSO for mixed-mode authentication (Forms and Windows)

1. SSO for parent and child application in the virtual sub-directory

Lets assume that we have two .NET applications - Foo and Bar, and Bar is running in a virtual sub-directory of Foo (http://foo.com/bar). Both applications implement Forms authentication. Implementation of Forms authentication requires you to override the Application_AuthenticateRequest, where you perform the authentication and upon successful authentication, call FormsAuthentication.RedirectFromLoginPage, passing in the logged-in user name (or any other piece of information that identifies the user in the system) as a parameter. In ASP.NET the logged-in user status is persisted by storing the cookie on the client computer. When you call RedirectFromLoginPage, a cookie is created which contains an encrypted FormsAuthenticationTicket with the name of the logged-in user . There is a section in web.config that defines how the cookie is created:

<authentication mode="Forms">
   <
forms name=".FooAuth" protection="All" timeout="60" loginUrl="login.aspx" />
</
authentication>

 <authentication mode="Forms">
   <
forms name=".BarAuth" protection="All" timeout="60" loginUrl="login.aspx" />
</
authentication>

The important attributes here are name and protection. If you make them match for both Foo and Bar applications, they will both write and read the same cookie using the same protection level, effectively providing SSO:

<authentication mode="Forms">
   <
forms name=".SSOAuth" protection="All" timeout="60" loginUrl="login.aspx" />
</
authentication>

When protection attribute is set to "All", both encryption and validation (via hash) is applied to the cookie. The default validation and encryption keys are stored in the machine.config file and can be overridden in the application’s web.config file. The default value is this:

<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey=" AutoGenerate,IsolateApps" validation="SHA1" />

IsolateApps means that a different key will be generated for every application. We can’t have that. In order for the cookie to be encrypted and decrypted with the same key in all applications either remove the IsolateApps option or better yet, add the same concrete key to the web.config of all applications using SSO:

<machineKey validationKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902" decryptionKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923AC" validation="SHA1" />

If you are authenticating against the same users store, this is all it takes – a few changes to the web.config files.

2. SSO using different authorization credentials (username mapping)

But what if the Foo application authenticates against its own database and the Bar application uses Membership API or some other form of authentication? In this case the automatic cookie that is created on the Foo is not going to be any good for the Bar, since it will contain the user name that makes no sense to the Bar.

To make it work, you will need to create the second authentication cookie especially for the Bar application. You will also need a way to map the Foo user to the Bar user. Lets assume that you have a user "John Doe" logging in to the Foo application and you determined that this user is identified as "johnd" in the Bar application. In the Foo authentication method you will add the following code:

FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, "johnd", DateTime.Now, DateTime.Now.AddYears(1), true, "");
HttpCookie cookie = new HttpCookie(".BarAuth");
cookie.Value =
FormsAuthentication.Encrypt(fat);
cookie.Expires = fat.Expiration;
HttpContext.Current.Response.Cookies.Add(cookie);

FormsAuthentication.RedirectFromLoginPage("John Doe");

User names are hard-coded for demonstration purposes only. This code snippet creates the FormsAuthenticationTicket for the Bar application and stuffs it with the user name that makes sense in the context of the Bar application. And then it calls RedirectFromLoginPage to create the correct authentication cookie for the Foo application. If you changed the name of forms authentication cookie to be the same for both applications (see our previous example), make sure that they are different now, since we are not using the same cookie for both sites anymore:

<authentication mode="Forms">
   <
forms name=".FooAuth" protection="All" timeout="60" loginUrl="login.aspx" slidingExpiration="true"/>
</
authentication>

 <authentication mode="Forms">
   <
forms name=".BarAuth" protection="All" timeout="60" loginUrl="login.aspx" slidingExpiration="true"/>
</
authentication>

Now when the user is logged in to Foo, he is mapped to the Bar user and the Bar authentication ticket is created along with the Foo authentication ticket. If you want it to work in the reverse direction, add similar code to the Bar application:

FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, "John Doe", DateTime.Now, DateTime.Now.AddYears(1), true, "");
HttpCookie cookie = new HttpCookie(".FooAuth");
cookie.Value =
FormsAuthentication.Encrypt(fat);
cookie.Expires = fat.Expiration;
HttpContext.Current.Response.Cookies.Add(cookie);

FormsAuthentication.RedirectFromLoginPage("johnd");

Also make sure that you have the <machineKey> element in web.config of both applications with matching validation and encryption keys.

3. SSO for two applications in two sub-domains of the same domain

Now what if Foo and Bar are configured to run under different domains http://foo.com and http://bar.foo.com. The code above will not work because the cookies will be stored in different files and will not be visible to both applications. In order to make it work, we will need to create domain-level cookies that are visible to all sub-domains. We can’t use RedirectFromLoginPage method anymore, since it doesn’t have the flexibility to create a domain-level cookie. So we do it manually:

FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, "johnd", DateTime.Now, DateTime.Now.AddYears(1), true, "");
HttpCookie cookie = new HttpCookie(".BarAuth");
cookie.Value =
FormsAuthentication.Encrypt(fat);
cookie.Expires = fat.Expiration;
cookie.Domain =
".foo.com";
HttpContext.Current.Response.Cookies.Add(cookie);

FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, "John Doe", DateTime.Now, DateTime.Now.AddYears(1), true, "");
HttpCookie cookie = new HttpCookie(".FooAuth");
cookie.Value =
FormsAuthentication.Encrypt(fat);
cookie.Expires = fat.Expiration;
cookie.Domain =
".foo.com";
HttpContext.Current.Response.Cookies.Add(cookie);

Note the highlighted lines. By explicitly setting the cookie domain to ".foo.com" we ensure that this cookie will be visible in both http://foo.com and http://bar.foo.com or any other sub-domain. You can also specifically set the Bar authentication cookie domain to "bar.foo.com". It is more secure, since other sub-domains can’t see it now. Also notice that RFC 2109 requires two periods in the cookie domain value, therefore we add a period in the front – ".foo.com"

Again, make sure that you have the same <machineKey> element in web.config of both applications. There is only one exception to this rule and it is explained in the next secion.

4. SSO when applications run under different versions of .NET

It is possible that Foo and Bar applications run under different version of .NET. In this case the above examples will not work. It turns out that ASP.NET 2.0 is using a different encryption method for authorization tickets. In ASP.NET 1.1 it was 3DES, in ASP.NET 2.0 it is AES. Fortunately, a new attribute was introduced in ASP.NET 2.0 for backwards compatibility

<machineKey validationKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902" decryptionKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923AC" validation="SHA1" decryption="3DES" />

Setting decryption="3DES" will make ASP.NET 2.0 use the old encryption method, making the cookies compatible again. Don’t try to add this attribute to the web.config of the ASP.NET 1.1 application. It will cause an error.

5. SSO for two applications in different domains.

We’ve been quite successful creating shared authentication cookies so far, but what if Foo and Bar are in different domains – http://foo.com and http://bar.com? They cannot possibly share a cookie or create a second cookie for each other. In this case each site will need to create its own cookies, and call the other site to verify if the user is logged in elsewhere. One way to do it is via a series of redirects.

In order to achieve that, we will create a special page (we’ll call it sso.aspx) on both web sites. The purpose of this page is to check if the cookie exists in its domain and return the logged in user name, so that the other application can create a similar cookie in its own domain. This is the sso.aspx from Bar.com:

<%@ Page Language="C#" %>

<script language="C#" runat="server">

void Page_Load()
{
   
// this is our caller, we will need to redirect back to it eventually
   
UriBuilder uri = new UriBuilder(Request.UrlReferrer);

   HttpCookie c = HttpContext.Current.Request.Cookies[".BarAuth"];

   if (c != null && c.HasKeys) // the cookie exists!
   {
      
try
      
{
         
string cookie = HttpContext.Current.Server.UrlDecode(c.Value);
         
FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(cookie);         

         uri.Query = uri.Query + "&ssoauth=" + fat.Name; // add logged-in user name to the query
      
}
      
catch
      
{
      }
   }
   Response.Redirect(uri.ToString());
// redirect back to the caller
}

</script>

This page always redirects back to the caller. If the authentication cookie exists on Bar.com, it is decrypted and the user name is passed back in the query string parameter ssoauth.

On the other end (Foo.com), we need to insert some code into the http request processing pipeline. It can be in Application_BeginRequest event or in a custom HttpHandler or HttpModule. The idea is to intercept all page requests to Foo.com as early as possible to verify if authentication cookie exists:

1. If authentication cookie exists on Foo.com, continue processing the request. User is logged in on Foo.com
2. If authentication cookie doesn’t exist, redirect to Bar.com/sso.aspx.
3. If the current request is the redirect back from Bar.com/sso.aspx, analyse the ssoauth parameter and create an authentication cookie if necessary.

It looks pretty simple, but we have to watch out for infinite loops:

// see if the user is logged in
HttpCookie c = HttpContext.Current.Request.Cookies[".FooAuth"];

if (c != null && c.HasKeys) // the cookie exists!
{
   try
   
{
      string cookie = HttpContext.Current.Server.UrlDecode(c.Value);
      FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(cookie);
      return; // cookie decrypts successfully, continue processing the page
   
}
   
catch
   
{
   
}
}

// the authentication cookie doesn't exist - ask Bar.com if the user is logged in there
UriBuilder uri = new UriBuilder(Request.UrlReferrer);

if (uri.Host != "bar.com" || uri.Path != "/sso.aspx") // prevent infinite loop
{
   Response.Redirect(
http://bar.com/sso.aspx);
}
else
{
   // we are here because the request we are processing is actually a response from bar.com

   if (Request.QueryString["ssoauth"] == null)
   {
      // Bar.com also didn't have the authentication cookie
      
return; // continue normally, this user is not logged-in 
   
} else
   
{

      // user is logged in to Bar.com and we got his name!
      
string userName = (string)Request.QueryString["ssoauth"];
   
      
// let's create a cookie with the same name
      
FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, userName, DateTime.Now, DateTime.Now.AddYears(1), true, "");
      
HttpCookie cookie = new HttpCookie(".FooAuth");
      cookie.Value =
FormsAuthentication.Encrypt(fat);
      cookie.Expires = fat.Expiration;
      
HttpContext.Current.Response.Cookies.Add(cookie);
   
}
}

The same code should be placed on both sites, just make sure you are using correct cookie names (.FooAuth vs. .BarAuth) on each site. Since the cookie is not actually shared, the applications can have different <machineKey> elements. It is not necessary to synchronize the encryption and validation keys.

Some of us may cringe at the security implications of passing the user name in the query string. A couple of things can be done to protect it. First of all we are checking the referrer and we will not accept the ssoauth parameter from any source other then bar.com/sso.aspx (or foo.com/ssp.aspx). Secondly, the name can easily be encrypted with a shared key. If Foo and Bar are using different authentication mechanisms, additional user information (e.g. e-mail address) can be passed along similarly.

6. SSO for mixed-mode authentication (Forms and Windows)

So far we have dealt with Forms authentication only. But what if we want to authenticate Internet users via Forms authentication first and if it fails, check if the Intranet user is authenticated on the NT domain? In theory we can check the following parameter to see if there is a Windows logon associated with the request:

Request.ServerVariables["LOGON_USER"]

However, unless our site has Anonymous Access disabled, this value is always empty. We can disable Anonymous Access and enable Integrate Windows Authentication for our site in the IIS control panel. Now the LOGON_USER value contains the NT domain name of the logged in Intranet user. But all Internet users get challenged for the Windows login name and password. Not cool. We need to be able to let the Internet users login via Forms authentication and if it fails, check their Windows domain credentials.

One way to solve this problem is to have a special entry page for Intranet users that has Integrate Windows Authentication enabled, validates the domain user, creates a Forms cookie and redirects to the main web site. We can even conceal the fact that Intranet users are hitting a different page by making a Server.Transfer.

There is also an easier solution. It works because of the way IIS handles the authentication process. If anonymous access is enabled for a web site, IIS is passing requests right through to the ASP.NET runtime. It doesn’t attempt to perform any kind of authentication. However, if the request results in an authentication error (401), IIS will attempt an alternative authentication method specified for this site. You need to enable both Anonymous Access and Integrated Windows Authentication and execute the following code if Forms authentication fails:

if (System.Web.HttpContext.Current.Request.ServerVariables["LOGON_USER"] == "") { 
   System.Web.HttpContext.Current.Response.StatusCode = 401; 
   System.Web.HttpContext.Current.Response.End();
}
else
{
   
// Request.ServerVariables["LOGON_USER"] has a valid domain user now!
}

When this code executes, it will check the domain user and will get an empty string initially. It will then terminate the current request and return the authentication error (401) to IIS. This will make the IIS use the alternative authentication mechanism, which in our case is Integrated Windows Authentication. If the user is already logged in to the domain, the request will be repeated, now with the NT domain user information filled-in. If the user is not logged in to the domain, he will be challenged for the Windows name/password up to 3 times. If the user cannot login after the third attempt, he will get the 403 error (access denied).

Conclusion

We have examined various scenarios of Single Sign-On for two ASP.NET applications. It is also quite possible to implement SSO for heterogeneous systems spawning across different platforms. Ideas remain the same, but the implementation may require some creative thinking.

Tags: ,

Windows Mobile开发环境搭建指南

From http://blog.joycode.com/musicland/articles/38275.aspx

内容简介

本文描述了如何搭建典型的Windows Mobile开发环境。介绍了相应的IDESDK及辅助开发工具的安装及常用配置,并对Windows Mobile下一代开发环境的发展前景做出展望。本文适合初次接触Windows Mobile开发的开发人员。

Windows Mobile开发简介

Windows Mobile是微软公司操作系统产品线上重要的一环。与Windows 9x/NT/XP/2003相比,Windows Mobile作为完整产品线出现在公众面前的时间非常短,但它的发展却异常迅速,而在这一发展过程中,开发人员提供了绝对关键的推动力。现在,越来越多的开发人员(特别是.NET开发人员)希望能尽快进入Windows Mobile的开发领域,而他们所面临的首要问题,除了熟悉Windows Mobile平台特性之外,便是如何搭建适合的开发环境。因此,本文就将从如何搭建一个Windows Mobile开发环境入手,对Windows Mobile平台的开发特性进行概要描述,以期帮助开发人员迅速进入Windows Mobile开发领域。

在展开本文的内容之前需要说明的一点是,Windows Mobile平台开发主要包含以下两方面内容:

l         平台级开发

l         应用级开发

因为Windows Mobile平台构建于微软的Windows CE系列操作系统之上(当前正式商用版本为Windows CE .NET 4.21),而Windows CE操作系统具有非常良好的模块化特性,因此开发人员,特别是手机设备厂商,可以通过微软提供的Platform BuildereMbedded Visual C++(以下简称为eVC)非常方便地定制自己所需的操作系统。这对应的是上面所说的平台级开发。

然而,绝大多数的开发人员并不需要去构建一个全新的、个性化的系统,他们只需要关心如何构建能够运行在Windows Mobile设备上的应用程序。为此,微软按主要智能设备自身硬件设备特性的不同以及用户体验的差异,定制出了Windows CE .NET 4.x系列操作系统的两个主要分支,分别安装在不同的Windows Mobile硬件设备中,从而也就构成了我们通常所说的Pocket PCSmartphone。以下内容中的Windows Mobile开发,除特指之外,均表示Pocket PC/Smartphone开发。

本文中提供的大量开发工具,除单独标出的以外,均可在http://msdn.microsoft.com/mobility/downloads/default.aspx下载。

迅速搭建开发环境

为了进行Windows Mobile开发,我们需要搭建相应的开发环境。所幸的是,这一环境搭建起来非常简单。

安装Visual Studio .NET 2003

为了降低Windows Mobile的开发难度,提高开发人员的开发效率,微软把.NET Framework移植到了Windows Mobile设备上,针对Windows Mobile设备及Windows CE .NET操作系统设计了.NET Compact Framework(以下简称.NET CF)。而Visual Studio .NET 2003正是开发.NET CF应用程序的最佳IDE。在Visual Studio .NET 2003的安装中默认集成了.NET CF 1.0,同时提供了Pocket PC 2002的开发模板,可以直接用来开发Pocket PC 2002应用程序。

您也可以选择Visual Studio 2005作为开发工具,目前最高版本为Beta 1 Refresh。在Visual Studio 2005中,默认提供了Pocket PC/Smartphone 2003 SE(第二版)的开发模板,同时将Pocket PC 2003 SE开发使用.NET CF版本升级到2.0 (beta)

安装eVC (可选)

以上环境已经完全可以基于.NET CF开发Windows Mobile应用程序。但如果您想使用C/C++语言来进行native开发,或者想在Windows Mobile设备上开发COM组件及应用,那么可以选择eVC作为开发工具。目前eVC的最高版本为4.0 + SP4,请确保安装了最新的补丁包。

也许您会问:既然已经有了Visual Studio .NET环境,为什么还要使用eVC作为开发工具呢?这主要是因为,当前的.NET CF功能相对而言还很不完整,有很多Windows CE .NET操作系统的核心功能没有被封装入.NET CF,如:加密/解密、网络底层通讯、COM互操作、RAPI等等。为了完成上述操作,我们或者使用平台调用(P/Invoke)以托管方式开发,或者使用eVC直接针对系统底层进行native开发。对于COM互操作,目前.NET CF还不提供COM互操作(Interop)机制,只能使用eVC进行C/C++ COM开发。好消息是,在.NET CF 2.0中将提供功能强大的COM互操作特性支持,同时也将在Visual C++ 2005环境中同时提供managednative开发功能,这样所有的开发工作将完全整合在Visual Studio 2005同一环境中。

安装Pocket PC/Smartphone 2003 SDK

为了开发Pocket PC 2003Smartphone 2003应用程序,您还需要安装Pocket PC 2003 SDKSmartphone 2003 SDK。值得注意的是,这两款SDK中所包含的模拟器均为英语环境,如果您想使用简体中文环境的模拟器,请去微软网站上单独下载。

如果您使用Visual Studio 2005开发Windows Mobile应用程序,则不需要单独安装相应的SDK,因为SDK已经被默认集成至了Visual Studio 2005环境中。

SQL Server CE (可选)

这是Pocket PC上推荐使用的嵌入式数据库产品,它与SQL Server桌面版之间可以实现良好的数据同步及互操作,目前正式版的最新版本为2.0。在SQL Server 2005 Beta 2测试版中,包含有SQL Server CE 2.0的升级版本SQL Server 2005 Mobile Edition——不过该版本目前只支持Pocket PC 2003系统。预计SQL Server Mobile Edition在下一个版本中才能提供对Smartphone的支持。

ActiveSync环境搭建

在完成了上述开发环境的搭建之后,您就可以在Visual Studio .NET中开发和调试应用程序,并在设备/模拟器中进行部署。但是,如果您还需要进一步执行下述操作,那么请按本节所述继续搭建ActiveSync环境。可能的执行操作包括(但不限于):

l         向设备/模拟器中复制文件

l         从设备/模拟器中删除文件

l         与设备/模拟器同步数据

l         开发和调试包含RAPI (Remote API)功能的应用

l         ……

安装ActiveSync

ActiveSync是一款管理Windows Mobile设备连接、与PC进行数据同步的工具。如果您开发时使用的是真实设备,则最好事先安装ActiveSync使之与PC机连接。如果您使用的是模拟器,则除了安装ActiveSync以外,还需要安装下述工具。

安装Connect Emulator with ActiveSync

如果您使用的是模拟器软件,除了安装ActiveSync外,还必须安装Connect Emulator with ActiveSync这款工具软件。它将使您可以直接通过ActiveSync连接到模拟器上。

辅助工具介绍

除了上述基本开发工具以外,在Windows Mobile开发社区中还存在着大量的辅助工具,包括应用程序框架、工具、控件等等。其中有一些由于定位准确、制作优良、使用方便,已经成为Windows Mobile开发不可或缺的好助手。以下列出其中的一部分,更多的还存在于Windows Mobile开发者社区及互联网上,等待着您去发现。

OpenNETCF.org Smart Device Framework

由几位长年活跃于Windows Mobile开发者社区的MVP共同开发的OpenNETCF.org Smart Device Framework (以下简称为SDF),几天前刚刚获得了Pocket PC Magazine评出的2004年度最佳.NET开发框架大奖。熟悉SDF的开发者都会认为,对于这一奖项,OpenNETCF的确当之无愧,因为SDF几乎已经成了Windows Mobile开发者不可或缺的工具。他们对.NET CF的不足给出了及时的弥补,在SDF中,您可以找到ConfigurationXML Serialization NotificationInterop ServicesWSE 2.0等一系列.NET CF当前版本尚未提供的功能,以及诸如ListBoxEx等优秀的控件。更值得一提的是,SDF是一项完全免费的开源项目。这几位MVP为整个Windows Mobile开发者社区所做的贡献也许真的无法用金钱来衡量。

下载地址:http://www.opennetcf.org

Windows Mobile Developer Power Toys

这是微软继Pocket PC/Smartphone 2003 SDK之后为开发人员提供的另一套开发辅助工具集。其中包含不少非常值得一用的优秀工具,如:

l         ActiveSync Remote Display:把连接在PC上的智能设备影像通过PC屏幕放大显示

l         CECopy:以命令行的方式向所连接的智能设备传输文件

l         Hopper:用于Pocket PC 2003的用户输入压力测试工具

l         PPC Command Shell:用于Pocket PC 2003的命令行工具

l         TypeIt:用于Smartphone 2003的辅助文字输入工具

l         ……

Mobile Application Development Toolkit 2004

这是微软近期推出的Windows Mobile开发工具及资料集,其中包含上述所有产品及工具,以及一系列相关的文档、教程及参考资料。

结束语

好了,对于如何搭建Windows Mobile的开发环境就介绍到这里了。可以肯定地说,随着微软公司支持力度的增加和Windows Mobile开发者社区的成熟,Windows Mobile开发将面临越来越有利的良好环境。

25 Ways To Improve Your Site Today

From http://fadtastic.net/2007/06/10/25-ways-to-improve-your-site-today/

Yes, the title may look like this post should be on an amateur blog and that it will be full of references to clip art and animated gifs, but this is serious. I’ve compiled a list of what I think are 25 ways to improve your website in as little time as possible. All can be done in a matter of minutes. Now, a website is hard work and usually there are no quick fixes but this list should provide you with a few pointers to make some updates today. If you like, it can also be used as a basis for a quality check document.

Usability/Accessibility

Because it matters. If people can’t use your site, they won’t stay.

  1. Navigation: Ensure that your navigation is easy to use and consistent. You may be able to use it, but could a newcomer find the information they desire?
  2. Search: If you don’t have a search box, then why not? Sometimes navigation isn’t enough. It may not be a 5 minute job to add a custom search facility, but it is extremely quick to add a third party search like Google’s.
  3. Click here: Why? Change this phrase everywhere on your site. It doesn’t make sense out of context. The user has to read the whole paragraph (which they probably won’t) to understand why they should click there. Consider phrases like “Download the profit/loss graph” or “Listen to the podcast entitled food for thought.”
  4. Title & Alt Attributes: Use them how they are supposed to be used. If you haven’t used any at all, then a quick fix will be to start adding them to navigation and other elements on every page.
  5. General: If you’ve got any code snippets that could annoy the user, like resizing browser windows or opening new pages in a new window, then remove them. Just because you like something a particular way, your users may not. Don’t take over their desktop.

Search Engine Optimisation - SEO

Because you want to be found. (On-page tips only here)

  1. Titles: Add consistent, relevant but different titles (title tag) to every page in your site. If you already have titles, check that they are short and describe the content of the page.
  2. Link around: Internal links to your pages (I’m thinking from the body copy here) are just as important as external links. Besides the usability gain, you have the unique opportunity of specifying your link and title text.
  3. Strengthen keywords: Probably the quickest thing you can do. Highlight some keywords and phrases and add a strong tag (bold) around them. This shouldn’t be overused but can provide some positive results if used sparingly.
  4. Headers: The correct use of headers (in a semantic, logical manner) can produce fantastic results. The H1 tag is the most important - try using some keywords in there.
  5. Overkill: It seems whenever I speak to someone about SEO and give them some tips, they go way overboard. This can really ruin all the hard work you’ve done for the user. So remember, optimise with the user in mind. And stay away from dodgy SEO stuff - every time you use black-hat SEO, a kitten steps on broken glass.

Design/Development Process

Design is our passion. Let it show.

  1. Images: By all means create beautiful images, but don’t forget to optimise them for the web. You may have a quick connection - not everybody does.
  2. Design comes from scratch(pad): Don’t necessarily fire up your graphics editor before thinking about the design. It can only take 10 minutes to draw (you know - pencil and paper) various layouts and wireframe the design. Doing this will make you think more about placement of elements and less about the aesthetics.
  3. Contrast/Text-size: Ensure that contrast levels and text sizes are ‘acceptable’ - There are no golden figures (although recommendations are available) to aim for but at least check with other people using different setups. Just because you have perfect 20:20 vision doesn’t mean that anyone can read your site.
  4. Consistency: There’s nothing worse (OK - an exaggeration, yes) than a website that doesn’t function consistently. A user can find it hard enough to learn how a website is put together without having to remember all the little quirks and foibles on your site.
  5. Testing: Check (or get others to check) the site under as many conditions as possible. Try to do this every time the site has a significant update. It’s worth it as it only takes a minute or two.

Content

That’s why people visit the site.

  1. Text/Whitespace: There’s a big trap that often people fall into. Whitespace. How many times have you heard “we need to fill that space and cram the text in a bit more” ? More text in a single area isn’t a good thing. It can make it harder and less enjoyable to read the content. It you had a shop would you cram as many products in as possible? No. You’d let them have space so they get noticed. Do the same with your text.
  2. Write for your audience: Can people understand your text? Think about your audience and reword those confusing sentences.
  3. Corporate Boasting: Which is more useful ~ “We provide 200 megaunits of wobble-sprockets to our worldwide, global markets” ~ or ~ “We can provide a wobble-sprocket to you anywhere in the world.” Speak to the reader and engage them. Don’t waffle.
  4. Use interest: If someone has read an article or item of content, then it is fair to assume that they were interested. Instead of leaving them high-and-dry after an article, point them somewhere related, whether it be another article or a product perhaps.
  5. Objectives: When writing content, make a little mental note of what your goal is for the page (e.g. encourage registration) and try to guess what the users goal is (e.g. to get at information.) Match the two (e.g. “to find this information simply sign up”) and you’re golden.

Off-Site

Because it’s not all about you.

  1. Hang out: For reasons surrounding traffic and respect. Go to forums, blogs and portals within your niche and hang out. Offer advice, link up with people and gain respect. Doing this for 10 minutes a day will improve your image and lead to quality, niche-lead traffic. A bonus. Oh, and don’t spam your community.
  2. Learn: As well as handing out advice, listen. Whether it be listening to colleagues, competition or potential clients you are bound to learn an awful lot just by witnessing other people’s actions.
  3. Encourage viral promotion: Not particularly in a gimmicky-email-newsletter fashion (that takes budget and time) but in a social fashion. Tell your friends and colleagues (and clients if relevant) something unique about you, your company or website and chances are they’ll pass it on. 5 minutes of blabbering could lead to heaps of traffic.
  4. Be yourself: When interacting online, don’t conform to internet stereotypes - just be yourself. Doing this will mean that you are natural when interacting online and more likely to take a similar approach as you would offline. So, take your offline business ways online.
  5. Spam: As in don’t spam. Every time you spam, you are adding disrespect to your own company/site. It’s the real-world equivalent of pushing a leaflet into a potential customer’s mouth.

And there we go. I hope this list is useful and has shown you how easy it is to improve various aspects of your website and its marketing. This isn’t exhaustive by any means - as I said earlier it takes effort and time to really get your site near-perfect. Evolution is the key: tweak, feedback, measure and repeat. Oh, and before you go looking, no, I don’t always practice what I preach!

Tags: ,

Jun. 13th, 2007

~/.vimrc

set ruler
set autoindent
set smartindent
set tabstop=4
set shiftwidth=4
set showmatch
set nohls
set incsearch

Type :help options within vim to get a complete list of options.

It seems that ubuntu 7.04 doesn't have ncurses lib

Just write a simple test program,

#include
[Error: Irreparable invalid markup ('<ncurses.h>') in entry. Owner must fix manually. Raw contents below.]

Just write a simple test program,

#include <ncurses.h>

int main()
{
initscr(); /* Start curses mode */
printw("Hello World !!!"); /* Print Hello World */
refresh(); /* Print it on to the real screen */
getch(); /* Wait for user input */
endwin(); /* End curses mode */

return 0;
}

However,
user@localhost:~/progs$ gcc test.c -lncurses
test.c:1:21: error: ncurses.h: No such file or directory

I tried apt-get to install ncurses, but failed.
user@localhost:~/progs$ sudo apt-get install ncurses
Password:
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package ncurses is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
E: Package ncurses has no installation candidate

So I manually apt-get installed the packages listed here(http://packages.ubuntu.com/hoary/source/ncurses) one by one. Now I can compile my ncurses programs.

Starting Ncurses

History:
termcap -> terminfo -> curses -> ncurses

Terminal Capability
Terminal Info
Cursor Optimization
New curses

In short, NCURSES is a library of functions that manages an application's display on character-cell terminals.

Document Download: NCURSES Programming HOWTO
http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html/NCURSES-Programming-HOWTO-html.tar.gz
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/

Another tutorial: Writing Programs with NCURSES
http://web.cs.mun.ca/~rod/ncurses/ncurses.html
http://invisible-island.net/ncurses/ncurses-intro.html

A Chinese Guy dedicated to ncurses
http://blog.csdn.net/byronm/
Tags:

试试这个windows客户端的中文支持

如题。

Try LiveJournal Windows Client

Semagic 1.7.0.5U
http://semagic.sourceforge.net/index.html

在 Ubuntu Linux 上安装 Java 和 Eclipse

http://blog.csdn.net/holy_phoenix/archive/2006/09/04/1176871.aspx

Livejournal client

For GNOME:
drivel
http://www.dropline.net/drivel/

For KDE:
ljKlient
http://ljklient.sourceforge.net/

It seems ljKlient is much better than drivel in regard to many functions, such as Tag, and friend list.

Mar. 22nd, 2007

locale详解(zz)

locale 是国际化与本土化过程中的一个非常重要的概念,个人认为,对于中文用户来说,通常会涉及到的国际化或者本土化,大致包含三个方面:看中文,写中文,与 window中文系统的兼容和通信。从实际经验上看来,locale的设定与看中文关系不大,但是与写中文,及window分区的挂载方式有很密切的关系。本人认为就像一个纯英文的Windows能够浏览中文,日文或者意大利文网页一样,你不需要设定locale就可以看中文。那么,为什么要设定 locale呢?什么时候会用到locale呢?

Tags: locale 设定 原因 解释

一、为什么要设定locale 正如前面我所讲的,设定locale与你能否浏览中文的网页没有直接的关系,即便你把locale设置成en_US.ISO-8859-1这样一个标准的英文locale你照样可以浏览中文的网页,只要你的系统里面有相应的字符集(这个都不一定需要)和合适的字体(如simsun),浏览器就可以把网页翻译成中文给你看。具体的过程是网络把网页传送到你的机器上之后,浏览器会判断相应的编码的字符集,根据网页采用的字符集,去字体库里面找合适的字体,然后由文字渲染工具把相应的文字在屏幕上显示出来。

在下文本人会偶尔把字符集比喻成密码本,个人觉得对于一些东西比较容易理解,假如你不习惯的话,把全文copy到任何文本编辑器,用字符集替换密码本即可。

那有时候网页显示乱码或者都是方框是怎么回事呢?个人认为,显示乱码是因为设定的字符集不对(或者没有相应的字符集),例如网页是用UTF-8编码的,你非要用GB2312去看,而系统根据GB2312去找字体,然后在屏幕上显示,当然是一堆的乱码,也就是说你用一个错误的密码本去翻译发给你的电报,当然内容那叫一个乱;至于有些时候浏览的网页能显示一部分汉字,但有很多的地方是方框,能够显示汉字说明浏览器已经正确的判断出了网页的编码,并在字体库里面找到了相应的文字,但是并不是每个字体库都包含某个字符集全部的字体的缘故,有些时候会显示不完全,找一个比较全的支持较多字符集的字体就可以了。

既然我能够浏览中文网页,那为什么我还要设定locale呢?

其实你有没有想过这么一个问题,为什么gentoo官方论坛上中文论坛的网页是用UTF-8编码的(虽然大家一直强烈建议用GB2312编码),但是新浪网就是用GB2312编码的呢?而Xorg的官方网页竟然是ISO-8859-15编码的,我没有设定这个locale怎么一样的能浏览呢?这个问题就像是你有所有的密码本,不论某个网站是用什么字符集编码的,你都可以用你手里的密码本把他们翻译过来,但问题是虽然你能浏览中文网页,但是在整个操作系统里面流动的还是英文字符。所以,就像你能听懂英语,也能听懂中文。 最根本的问题是:你不可以写中文。

当你决定要写什么东西的时候,首先要决定的一件事情是用那种语言,对于计算机来说就是你要是用哪一种字符集,你就必须告诉你的linux系统,你想用那一本密码本去写你想要写的东西。知道为什么需要用GB2312字符集去浏览新浪了吧,因为新浪的网页是用GB2312写的。

为了让你的Linux能够输入中文,就需要把系统的locale设定成中文的(严格说来是locale中的语言类别LC_CTYPE ),例如zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白这些古里古怪的表达方式。这个外星表达式规定了什么东西呢?这个问题稍后详述,现在只需要知道,这是locale的表达方式就可以了。

二、到底什么是locale? locale这个单词中文翻译成地区或者地域,其实这个单词包含的意义要宽泛很多。Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境。

这个用户环境可以按照所涉及到的文化传统的各个方面分成几个大类,通常包括用户所使用的语言符号及其分类(LC_CTYPE),数字 (LC_NUMERIC),比较和排序习惯(LC_COLLATE),时间显示格式(LC_TIME),货币单位(LC_MONETARY),信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES),姓名书写方式(LC_NAME),地址书写方式(LC_ADDRESS),电话号码书写方式 (LC_TELEPHONE),度量衡表达方式(LC_MEASUREMENT),默认纸张尺寸大小(LC_PAPER)和locale对自身包含信息的概述(LC_IDENTIFICATION)。

所以说,locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。一个地区的locale就是根据这几大类的习惯定义的,这些locale定义文件放在/usr /i18n/locales目录下面,例如en_US, zh_CN and de_DE@euro都是locale的定义文件,这些文件都是用文本格式书写的,你可以用写字板打开,看看里边的内容,当然出了有限的注释以外,大部分东西可能你都看不懂,因为是用的Unicode的字符索引方式。

对于de_DE@euro的一点说明,@后边是修正项,也就是说你可以看到两个德国的locale: /usr /i18n/locales/de_DE@euro /usr /i18n/locales/de_DE 打开这两个locale定义,你就会知道它们的差别在于de_DE@euro使用的是欧洲的排序、比较和缩进习惯,而de_DE用的是德国的标准习惯。

上面我们说到了zh_CN.GB18030的前半部分,后半部分是什么呢?大部分Linux用户都知道是系统采用的字符集。

三、什么是字符集?字符集就是字符,尤其是非英语字符在系统内的编码方式,也就是通常所说的内码,所有的字符集都放在/usr /i18n/charmaps,所有的字符集也都是用Unicode编号索引的。Unicode用统一的编号来索引目前已知的全部的符号。而字符集则是这些符号的编码方式,或者说是在网络传输,计算机内部通信的时候,对于不同字符的表达方式,Unicode是一个静态的概念,字符集是一个动态的概念,是每一个字符传递或传输的具体形式。就像 Unicode编号U59D0是代表姐姐的“姐”字,但是具体的这个字是用两个字节表示,三个字节,还是四个字节表示,是字符集的问题。例如:UTF-8 字符集就是目前流行的对字符的编码方式,UTF-8用一个字节表示常用的拉丁字母,用两个字节表示常用的符号,包括常用的中文字符,用三个表示不常用的字符,用四个字节表示其他的古灵精怪的字符。而GB2312字符集就是用两个字节表示所有的字符。需要提到一点的是Unicode除了用编号索引全部字符以外,本身是用四个字节存储全部字符,这一点在谈到挂载windows分区的时候是非常重要的一个概念。所以说你也可以把Unicode看作是一种字符集(我不知道它和UTF-32的关系,反正UTF-32就是用四个字节表示所有的字符的),但是这样表述符号是非常浪费资源的,因为在计算机世界绝大部分时候用到的是一个字节就可以搞定的 26个字母而已。所以才会有UTF-8,UTF-16等等,要不然大同世界多好,省了这许多麻烦。

四、zh_CN.GB2312到底是在说什么? Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言[_地域[.字符集]]. 所以说呢,locale总是和一定的字符集相联系的。下面举几个例子:

1、我说中文,身处中华人民共和国,使用国标2312字符集来表达字符。 zh_CN.GB2312=中文_中华人民共和国+国标2312字符集。

2、我说中文,身处中华人民共和国,使用国标18030字符集来表达字符。 zh_CN.GB18030=中文_中华人民共和国+国标18030字符集。

3、我说中文,身处中华人民共和国台湾省,使用国标Big5字符集来表达字符。 zh_TW.BIG5=中文_台湾.大五码字符集

4、我说英文,身处大不列颠,使用ISO-8859-1字符集来表达字符。 en_GB.ISO-8859-1=英文_大不列颠.ISO-8859-1字符集

5、我说德语,身处德国,使用UTF-8字符集,习惯了欧洲风格。 de_DE.UTF-8@euro=德语_德国.UTF-8字符集@按照欧洲习惯加以修正

注意不是de_DE@euro.UTF-8,所以完全的locale表达方式是 [语言[_地域][.字符集] [@修正值]

生成的locale放在/usr b/locale/目录中,并且每个locale都对应一个文件夹,也就是说创建了de_DE@euro.UTF-8 locale之后,就生成/usr b/locale/de_DE@euro.UTF-8/目录,里面是具体的每个locale的内容。

五、怎样去自定义locale 在gentoo生成locale还是很容易的,首先要在USE里面加入userlocales支持,然后编辑locales.build文件,这个文件用来指示glibc生成locale文件。 很多人不明白每一个条目是什么意思。 其实根据上面的说明现在应该很明确了。

File: /etc/locales.build en_US/ISO-8859-1 en_US.UTF-8/UTF-8

zh_CN/GB18030 zh_CN.GBK/GBK zh_CN.GB2312/GB2312 zh_CN.UTF-8/UTF-8

上面是我的locales.build文件,依次的说明是这样的:

en_US/ISO-8859-1:生成名为en_US的locale,采用ISO-8859-1字符集,并且把这个locale作为英文_美国locale类的默认值,其实它和en_US.ISO-8859-1/ISO-8859-1没有任何区别。

en_US.UTF-8/UTF-8:生成名为en_US.UTF-8的locale,采用UTF-8字符集。

zh_CN/GB18030:生成名为zh_CN的locale,采用GB18030字符集,并且把这个locale作为中文_中国locale类的默认值,其实它和zh_CN.GB18030/GB18030没有任何区别。

zh_CN.GBK/GBK:生成名为zh_CN.GBK的locale,采用GBK字符集。 zh_CN.GB2312/GB2312:生成名为zh_CN.GB2312的locale,采用GB2312字符集。 zh_CN.UTF-8/UTF-8:生成名为zh_CN.UTF-8的locale,采用UTF-8字符集。

关于默认locale,默认locale可以简写成en_US或者zh_CN的形式,只是为了表达简单而已没有特别的意义。

Gentoo在locale定义的时候掩盖了一些东西,也就是locale的生成工具:localedef。在编译完glibc之后你可以用这个localedef 再补充一些locale,就会更加理解locale了。具体的可以看 localedef 的manpage。

$localedef -f 字符集 -i locale定义文件 生成的locale的名称 例如 $localedef -f UTF-8 -i zh_CN zh_CN.UTF-8

上面的定义方法和在locales.build中设定zh_CN.UTF-8/UTF-8的结果是一样一样的。

六、locale的五脏六腑

刚刚生成了几个locale,但是为了让它们生效,必须告诉Linux系统使用那(几)个locale。这就需要对locale的内部机制有一点点的了解。在前面我已经提到过,locale把按照所涉及到的文化传统的各个方面分成12个大类,这12个大类分别是: 1、语言符号及其分类(LC_CTYPE) 2、数字(LC_NUMERIC) 3、比较和排序习惯(LC_COLLATE) 4、时间显示格式(LC_TIME) 5、货币单位(LC_MONETARY) 6、信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES) 7、姓名书写方式(LC_NAME) 8、地址书写方式(LC_ADDRESS) 9、电话号码书写方式(LC_TELEPHONE) 10、度量衡表达方式(LC_MEASUREMENT) 11、默认纸张尺寸大小(LC_PAPER) 12、对locale自身包含信息的概述(LC_IDENTIFICATION)。

其中,与中文输入关系最密切的就是 LC_CTYPE, LC_CTYPE 规定了系统内有效的字符以及这些字符的分类,诸如什么是大写字母,小写字母,大小写转换,标点符号、可打印字符和其他的字符属性等方面。而locale定义zh_CN中最最重要的一项就是定义了汉字(Class “hanzi”)这一个大类,当然也是用Unicode描述的,这就让中文字符在Linux系统中成为合法的有效字符,而且不论它们是用什么字符集编码的。

LC_CTYPE % This is a copy of the "i18n" LC_CTYPE with the following modifications: - Additional classes: hanzi

copy "i18n"

class "hanzi"; / % ..;/ ..;/ ;;;;;;;;/ ;;;;;;;;/ ;;;; END LC_CTYPE

在en_US的locale定义中,并没有定义汉字,所以汉字不是有效字符。所以如果要输入中文必须使用支持中文的locale,也就是zh_XX,如zh_CN,zh_TW,zh_HK等等。

另外非常重要的一点就是这些分类是彼此独立的,也就是说LC_CTYPE,LC_COLLATE和 LC_MESSAGES等等分类彼此之间是独立的,可以根据用户的需要设定成不同的值。这一点对很多用户是有利的,甚至是必须的。例如,我就需要一个能够输入中文的英文环境,所以我可以把LC_CTYPE设定成zh_CN.GB18030,而其他所有的项都是en_US.UTF-8。

七、怎样设定locale呢?

设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量:LC_ALL和LANG。它们之间有一个优先级的关系: LC_ALL>LC_*>LANG 可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。 1、如果你设定了LC_ALL=zh_CN.UTF-8,那么不管LC_*和LANG设定成什么值,它们都会被强制服从LC_ALL的设定,成为 zh_CN.UTF-8。 2、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*=en_US.UTF-8,并且没有设定LC_ALL的话,那么系统的locale 设定以LC_*=en_US.UTF-8。 3、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*,和LC_ALL均未设定的话,系统会将LC_*设定成默认值,也就是LANG的值 zh_CN.UTF-8 。 4、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_CTYPE=en_US.UTF-8,其他的LC_*,和LC_ALL均未设定的话,那么系统的locale设定将是:LC_CTYPE=en_US.UTF-8,其余的 LC_COLLATE,LC_MESSAGES等等均会采用默认值,也就是LANG的值,也就是LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=zh_CN.UTF-8。

所以,locale是这样设定的: 1、如果你需要一个纯中文的系统的话,设定LC_ALL= zh_CN.XXXX,或者LANG= zh_CN.XXXX都可以,当然你可以两个都设定,但正如上面所讲,LC_ALL的值将覆盖所有其他的locale设定,不要作无用功。 2、如果你只想要一个可以输入中文的环境,而保持菜单、标题,系统信息等等为英文界面,那么只需要设定LC_CTYPE=zh_CN.XXXX,LANG = en_US.XXXX就可以了。这样LC_CTYPE=zh_CN.XXXX,而LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=en_US.XXXX。 3、假如你高兴的话,可以把12个LC_*一一设定成你需要的值,打造一个古灵精怪的系统: LC_CTYPE=zh_CN.

Sep. 22nd, 2006

resolve wireless connection

1.At first got the warning message when dmesg

ieee80211_crypt: registered algorithm 'NULL'
ieee80211: 802.11 data/management/control stack, git-1.1.7
ieee80211: Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>
ipw2100: Intel(R) PRO/Wireless 2100 Network Driver, git-1.2.2
ipw2100: Copyright(c) 2003-2006 Intel Corporation
ACPI: PCI Interrupt 0000:02:03.0[A] -> Link [LNKB] -> GSI 5 (level, low) -> IRQ 5
ipw2100: Detected Intel PRO/Wireless 2100 Network Connection
ipw2100: eth1: Firmware 'ipw2100-1.3.fw' not available or load failed.
ipw2100: eth1: ipw2100_get_firmware failed: -2
ipw2100: eth1: Failed to power on the adapter.
ipw2100: eth1: Failed to start the firmware.
ipw2100Error calling register_netdev.
ACPI: PCI interrupt for device 0000:02:03.0 disabled
ipw2100: probe of 0000:02:03.0 failed with error -5

2.Check here with my CONFIGURATION.

1) Linux kernel satisfies.(That need some kernel tricky settings)
2) Download the newest Wireless Extensions and Tools(newest recommended in case some weird situation), i.e. iwconfig, iwevent, iwgetid, iwlist, iwpriv and iwspy.
3) I found I've already got the IPW2100 driver under /lib/modules/2.6.17.1/kernel/drivers/net/wireless/. Maybe they were generated while I compiled this new kernel. If not, download it from here.
4) Download the v1.3 IPW2100 binary firmware image from here and extract them into /lib/firmware.
And, *** Don't forget to add /usr/local/lib/ to /etc/ld.so.conf, and run ldconfig as root. ***

3.OKey! Now execute iwconfig and iw*** to configure the wireless connection.
This is the output of iwconfig
[root@localhost ~]# iwconfig
lo no wireless extensions.

eth0 no wireless extensions.

eth1 unassociated ESSID:off/any Nickname:"ipw2100"
Mode:Managed Channel=0 Access Point: Not-Associated
Bit Rate=0 kb/s Tx-Power:16 dBm
Retry min limit:7 RTS thr:off Fragment thr:off
Encryption key:off
Power Management:off
Link Quality:0 Signal level:0 Noise level:0
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:0 Invalid misc:0 Missed beacon:0

ppp0 no wireless extensions.

Sep. 20th, 2006

网通电信七宗罪!

【摘要】中国可生气的事数不胜数,不到忍无可忍,谁也不愿动肝火,到今天为止,我投诉互联互通问题刚好满一年,在此正式宣布投降,本文列举的两大电信运营商的七大罪状全是我亲身经历,作为庶民,我很生气,但后果一点也不严重,因为我连选择的余地都没有,当你厌倦了一个运营商想选择另一个时,却发现他们位于长江的另一边。
  中国可生气的事数不胜数,不到忍无可忍,谁也不愿动肝火,到今天为止,我投诉互联互通问题刚好满一年,在此正式宣布投降,本文列举的两大电信运营商的七大罪状全是我亲身经历,作为庶民,我很生气,但后果一点也不严重,因为我连选择的余地都没有,当你厌倦了一个运营商想选择另一个时,却发现他们位于长江的另一边。
  
  
  1. 互联不互通
  
  这个问题之经典,随便在长江南北找个人问问都知道,首先,这不是技术问题,如果说中国的软件和芯片行业比国外弱我信,互联网方面的技术,全球基本是一致的,说白了,诞生于上世纪中页的互联网从来都不是一个技术含量很高的东西,两大运营商有足够的能力将这件事做好,互联互通问题就象两个人对着头挖隧道,他们宁肯挖成两条隧道,也不肯在中间对接,最终,中国铺设了全世界最庞大的光缆系统,但这些光缆却没有给我们贡献多少实用的带宽,你打 10060 或 10000 抱怨网速,他们会信誓旦旦地说网速没有问题,你去桃花源找部电影看看慢吗,你访问新浪看看慢吗,诸如此类,在很多人看来,上网就是去看盗版电影或者浏览八卦新闻,但互联网上有无数的应用,很多应用需要全程带宽的支持,如果某个接点出现瓶颈,你没有权利说那是别的运营商的问题,即使是,你也有义务去协调,事实上互联互通问题已经存在了四年的时间,这四年,各大门户自己搞了负载均衡,下载站点开设了镜像,网吧开通了双路,倒霉的是家庭和企业的宽带用户,在这方面,你几乎没有任何可以做的事,网通和电信是不受理互联互通投诉的,他们会将你的问题混淆,转移,实在躲不过,就推给北京的高层。
  
  关于互联互通问题在中国已经严重到什么程度,我举一个例子,我的公司在青岛和广州分别有多台服务器托管在电信和网通的机房,其中青岛的服务器使用双 IP,一个 IP 属于网通,另一个属于电信,使用青岛网通的 IP PING 广州电信的服务器,时延是 700ms,改用电信的 PING,时延马上缩短为 10ms 以下;现在,我用青岛网通的 IP PING 同一个机房中使用电信 IP 的服务器,时延是 600ms,同一个机房的两台服务器位于同一个机架,它们之间的物理距离只有王家卫的 0.01 公分。另一个例子,为了远程登陆我广州的服务器,我需要首先登陆位于澳门的另一台服务器,再跳到广州,这样连接的速度,是直接连接的 20 倍。关于互联互通,网通的一个客服曾说过一句很经典的话,“陈天桥比你厉害吧,他的网络游戏公司也得接受这个现实。”
  
  
  2. 成为瓶颈的国际端口
  
  中国是鼓励老百姓老老实实待在家里,没事不要出门的,但互联网是一张全球的网,理论上没有地域概念,在国际互联网中,你没有办法独善其身。从 2005 年以来,我一直向网通抱怨国际端口瓶颈问题,但网通一直都在热火朝天地忙他们的宽带中国,在网通的一些视频点播网站,你差不多能用一两个小时就下载一部大片,却无法在同样的时间内从国外的服务器上收下一封几十K的邮件,同时,我的用户在国外出差的时候,半夜给我打电话,抱怨无法收邮件,而他们用远程技术登陆他们的办公室网络的时候,速度只有几百字节,相当于九十年代中期拨号的速度,网通的客服对这个问题的解释永远是爱国的,那就是,肯定是国外的网络出了问题,在网通看来,国外大概落后得不得了,为了证明国际端口的瓶颈问题,我特地在电信和网通的两个网络上做了一个测试,众所周知,电信的国际端口要快很多,我从广州向澳门(澳门是中国领土不可分割的一部分)做了一个 tracert,发现数据包先是传到北京,再从北京传到澳门,其中最大的一个越点时延是 4ms;同时,从青岛向澳门同一台服务器做了另一个 racert 测试,数据包也是先到达北京,再去澳门,而在这个 trace 中,最大的一个时延为 890 ms,这个延迟所在的越点正好位于北京和澳门之间。
  
  
  3. 关公战秦琼
  
  让多个运营商展开竞争,以提高电信服务质量,是当初电信拆分的初衷,这没有错,问题是,既然要竞争,为什么不放在同一个地区短兵交接,却让他们一南一北隔江而治,这中间信产部当然难辞其咎,但两大运营商也不该将错就错,偏安一方,电信拆分已经四年多了,中国的电信用户在资费和服务方面并没有得到多大的改善,与这种荒唐的竞争方式有直接的关系,在北方,如果你对网通不满,你唯一能做就是继续不满,并没有多少电信的网络资源供你选择,所以,如果你想对网通发火,最好先到楼道里看看那里有没有电信的接入盒,如果没有,赶紧收声吧。然而,如果你因此说网通的服务差,那也不对,事实上,网通的服务很到位,10060 基本随时可以接通,人工座席的声音也算甜美,一个服务请求一般会招来三到四个回访电话,这点和海尔颇有一拼,但海尔的皮毛好学,精髓难通,海尔在服务的时候基本不回避问题,他们的客服对任何问题都能说到点上,网通的客服却总是先将自己撇清。
  
  
  4. 唯利是图与泼脏水倒掉孩子
  
  电信和医疗教育一样,是国家垄断的公众资源,要求运营者拥有一定的社会责任,但网通和电信显然都掉进了钱眼,只要肯付钱,你做什么他们并不关心,这就是中国成为垃圾邮件天堂的原因。在世界几大反垃圾邮件组织(如 SORBS 和 ABUSE)的黑名单中,中国有成段成段的 IP 被列入,广州电信 IDC 的 IP 段在 2004 年曾被全球的邮件服务器抵制,我曾经多次给 SORBS 发邮件解释这个问题,也曾向香港的一家 ISP 交涉,在那段时间,我的用户几乎没有办法向国外发信,他们只要一听到广州电信 IDC 就连声说 NO,如今,广州电信又走入另一个极端,他们决心整治垃圾邮件服务器,整治的办法就是让非法与合法的邮件服务器一起玉石俱焚,大家知道,出于反垃圾邮件考虑,很多邮件服务器使用了反向 DNS 查询机制,以确认邮件的来源是否合法(因为 SMTP 协议是没有办法知道邮件域名和地址是否是伪造的),这就要求为你的邮件服务器的 IP 地址添加一条 PTR 记录,广州电信已经不受理这种业务了,即使受理,费用也高达 200元每月,厚黑到无以复加的程度。
  
  
  5. 事上而媚者必临下而骄
  
  企业和用户站在一起天经地义,因为你收了用户的钱,政府出于利己因素对国际互联网进行限制也无可厚非,但政府不是行业专家,作为电信运营商,你完全可以凭借自己的专业知识影响政府的决策,比如对国外某些站点的屏蔽,据我所知,很多被屏蔽的资源其实并没有什么问题,比如 wiki 和 dyndns,它们是纯粹的技术和文化性站点,即使有点杂音,也瑕不掩瑜,政府要实行屏蔽,首先要电信运营商合作,完全可以想象,网通和电信高级官僚是以怎样的媚骨迎合政府官员,没准还额外饶上几个。当你发现某些国外的站点无法访问的时候,你唯一能找的就是运营商,但他们根本提供不了任何帮助,他们首先要让他们的上级舒服,至于用户,先站一边吧。 wiki 无法访问已经超过半年了,顺便纪念一下。
  
  
  6. 先斩后奏或者不奏
  
  中国电信是否限制路由器共享上网,中国网通是否封杀 BT ,你的 10M 小区宽带实际带宽是多少,这都是些你明白,我明白,大家明白却没有人承认的问题。2004年,南方盛传中国电信使用了一种叫做网络尖兵的设备对路由器共享上网进行限制,当时我的一家企业用户已经被这个问题折磨的死去活来,我出面和当地的电信进行交涉,出人意料的是,他们对此一口否认,却回答不了为什么单机上网一切正常,一启用路由器就掉线的问题,为此,我亲自测试了市场上至少五款路由器,最后,一个客服实在看不下去,偷偷跟我说了一句同样很经典的话,“你说的问题,我也不说有,也不说没有,反正我们保证你的 ADSL 在一台电脑上是可用的,其他的你自己琢磨好了”。
  
  2005年底开始,网络有传北方网通开始封杀 BT,也是从那个时候开始,我的 Emule 开始出问题,只要一开 Emule,整个局域网就开始断断续续地离线,网通对此的解释仍旧是病毒,或者 Emule 对带宽的吞噬,但即使你将 Emule 的上下行连接速度限制到 10K 以下仍不起作用,经过研究,我发现只要将并发连接数减小到一定程度,马上会有改观,这印证了很多人对网通使用连接数封杀 BT 的猜测,而真正的内幕,也许无人得知,10060 对这件事的标准答复是非常安全的“尚未接到通知”。
  
  
  7. 永远的霸王
  
  消协对全国各行业霸王条款的调查显示,电信行业居首。电信行业的霸王条款多到数不清,网络中也有数不清的文章对这些霸王条款一条一条地进行揶揄,但无人能够撼动霸王的地位。几天前,我的家用 ADSL 准备续期,由于对 1M 的速率根本不满意,而中国互联网的瓶颈又根本不在这 1M 还是 2M,我便准备将 1M 的速率换成 512K,为了安全起见,我提出先试用 512K 一个月,如果有问题,再换回 1M,10060 马上提醒需要额外交 30 元手续费,我便举了很多例子,说明这 30 块实在没有道理,比如说吧,你去买茄子,说买一斤,然后又说买两斤,小贩会额外收你 30 块吗,不会;你去喝啤酒,说老板来两瓶,喝完了再加两瓶,人家会额外收你 30 块吗,不会;或者你去剃头,剃完了再刮刮脸,理发师会额外收你 30 块吗,也不会,那为什么你们要额外收这 30 块,网通的客服说,这是经物价局核定过的。一点题外话,内人就要生产了,上周陪她作 B 超,B 超费是 80 元,还要再交 10 元出报告,难道我出 80 元是让你们白看不成,在我们的国家,很多人信奉名正则言顺的道理,所以他们费尽心机为自己的不良行为找借口,因此我更欣赏强盗,他们要率真得多。

From 新京报

Intel® PRO/Wireless 2100 Driver for Linux

http://ipw2100.sourceforge.net/

Previous 20

July 2007

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
293031    

Advertisement

Syndicate

RSS Atom
Powered by LiveJournal.com